Co-authored-by: Clément Drouin <clement.drouin@mistral.ai>
Co-authored-by: Kim-Adeline Miguel <51720070+kimadeline@users.noreply.github.com>
Co-authored-by: Mathias Gesbert <mathias.gesbert@mistral.ai>
Co-authored-by: allansimon-mistral <allan.simon@ext.mistral.ai>
Co-authored-by: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
Clément Sirieix 2026-05-07 13:55:56 +02:00 committed by GitHub
parent 4972dd5694
commit b23f49e5f4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
74 changed files with 2342 additions and 240 deletions

View file

@ -101,6 +101,11 @@ def _build_env(vibe_home_dir: Path, *, include_api_key: bool) -> dict[str, str]:
env["PYTHONUNBUFFERED"] = "1"
env["VIBE_HOME"] = str(vibe_home_dir)
vibe_home_dir.mkdir(parents=True, exist_ok=True)
config_file = vibe_home_dir / "config.toml"
if not config_file.exists():
config_file.write_text("enable_telemetry = false\n")
if include_api_key:
env["MISTRAL_API_KEY"] = "mock"
else:

View file

@ -14,7 +14,7 @@ from vibe.acp.acp_agent_loop import VibeAcpAgentLoop
class TestCloseSession:
@pytest.mark.asyncio
async def test_close_session_removes_session_and_closes_resources(
self, acp_agent_loop: VibeAcpAgentLoop
self, acp_agent_loop: VibeAcpAgentLoop, telemetry_events: list[dict[str, Any]]
) -> None:
session_response = await acp_agent_loop.new_session(cwd=".", mcp_servers=[])
session = acp_agent_loop.sessions[session_response.session_id]
@ -30,6 +30,17 @@ class TestCloseSession:
assert session_response.session_id not in acp_agent_loop.sessions
backend_close.assert_awaited_once()
telemetry_close.assert_awaited_once()
session_closed_events = [
event
for event in telemetry_events
if event["event_name"] == "vibe.session_closed"
]
assert len(session_closed_events) == 1
assert (
session_closed_events[0]["properties"]["session_id"]
== session_response.session_id
)
assert session_closed_events[0]["properties"]["agent_entrypoint"] == "acp"
@pytest.mark.asyncio
async def test_close_session_cancels_active_prompt(
@ -69,6 +80,23 @@ class TestCloseSession:
assert bg_task.cancelled()
@pytest.mark.asyncio
async def test_emit_session_closed_for_active_sessions(
self, acp_agent_loop: VibeAcpAgentLoop, telemetry_events: list[dict[str, Any]]
) -> None:
session1 = await acp_agent_loop.new_session(cwd=".", mcp_servers=[])
session2 = await acp_agent_loop.new_session(cwd=".", mcp_servers=[])
await acp_agent_loop.emit_session_closed_for_active_sessions()
session_closed_events = [
e for e in telemetry_events if e["event_name"] == "vibe.session_closed"
]
emitted_ids = {e["properties"]["session_id"] for e in session_closed_events}
assert len(session_closed_events) == 2
assert session1.session_id in emitted_ids
assert session2.session_id in emitted_ids
@pytest.mark.asyncio
async def test_close_session_rejects_new_background_tasks(
self, acp_agent_loop: VibeAcpAgentLoop

View file

@ -12,6 +12,7 @@ from tests.stubs.fake_backend import FakeBackend
from tests.stubs.fake_client import FakeClient
from vibe.acp.acp_agent_loop import VibeAcpAgentLoop
from vibe.core.agent_loop import AgentLoop
from vibe.core.session.session_id import shorten_session_id
@pytest.fixture
@ -79,3 +80,10 @@ class TestCompactEventHandling:
assert compact_end.status == "completed"
assert compact_start.tool_call_id == compact_end.tool_call_id
assert compact_end.content is not None
compact_end_text = compact_end.content[0].content
assert isinstance(compact_end_text, TextContentBlock)
assert shorten_session_id(session_response.session_id) in compact_end_text.text
assert (
shorten_session_id(session.agent_loop.session_id) in compact_end_text.text
)

View file

@ -34,7 +34,7 @@ class TestACPInitialize:
),
)
assert response.agent_info == Implementation(
name="@mistralai/mistral-vibe", title="Mistral Vibe", version="2.9.4"
name="@mistralai/mistral-vibe", title="Mistral Vibe", version="2.9.5"
)
assert response.auth_methods == []
@ -62,7 +62,7 @@ class TestACPInitialize:
),
)
assert response.agent_info == Implementation(
name="@mistralai/mistral-vibe", title="Mistral Vibe", version="2.9.4"
name="@mistralai/mistral-vibe", title="Mistral Vibe", version="2.9.5"
)
assert response.auth_methods is not None

View file

@ -92,20 +92,26 @@ class TestMultiSessionCore:
)
assert user_message1 is not None
assert user_message1.content == "Prompt for session 1"
assistant_message1 = next(
(msg for msg in session1.agent_loop.messages if msg.role == Role.assistant),
None,
)
assert assistant_message1 is not None
assert assistant_message1.content == "Response 1"
user_message2 = next(
(msg for msg in session2.agent_loop.messages if msg.role == Role.user), None
)
assert user_message2 is not None
assert user_message2.content == "Prompt for session 2"
# Backend stream order is non-deterministic under asyncio.gather, so
# assert that both sessions received distinct responses from the
# expected set rather than pinning a specific assignment.
assistant_message1 = next(
(msg for msg in session1.agent_loop.messages if msg.role == Role.assistant),
None,
)
assistant_message2 = next(
(msg for msg in session2.agent_loop.messages if msg.role == Role.assistant),
None,
)
assert assistant_message1 is not None
assert assistant_message2 is not None
assert assistant_message2.content == "Response 2"
assert {assistant_message1.content, assistant_message2.content} == {
"Response 1",
"Response 2",
}

View file

@ -199,6 +199,28 @@ def test_callable_entries_updates_completions_dynamically() -> None:
assert suggestions[0].description == "Summarize the conversation"
def test_tab_on_slash_command_with_args_replaces_only_head() -> None:
controller, view = make_controller()
text = "/compact some args"
controller.on_text_changed(text, cursor_index=len(text))
result = controller.on_key(key_event("tab"), text=text, cursor_index=len(text))
assert result is CompletionResult.HANDLED
assert view.replacements == [Replacement(0, 8, "/compact")]
def test_enter_on_slash_command_with_args_submits_with_head_only_replacement() -> None:
controller, view = make_controller()
text = "/compact some args"
controller.on_text_changed(text, cursor_index=len(text))
result = controller.on_key(key_event("enter"), text=text, cursor_index=len(text))
assert result is CompletionResult.SUBMIT
assert view.replacements == [Replacement(0, 8, "/compact")]
def test_callable_entries_reflects_enabled_disabled_skills() -> None:
"""Test that skill enable/disable changes are reflected in completions.

View file

@ -42,7 +42,7 @@ async def test_popup_hides_when_input_cleared(vibe_app: VibeApp) -> None:
@pytest.mark.asyncio
async def test_pressing_tab_writes_selected_command_and_hides_popup(
async def test_pressing_tab_writes_selected_command_and_leaves_popup_visible(
vibe_app: VibeApp,
) -> None:
async with vibe_app.run_test() as pilot:
@ -53,7 +53,7 @@ async def test_pressing_tab_writes_selected_command_and_hides_popup(
await pilot.press("tab")
assert chat_input.value == "/config"
assert popup.styles.display == "none"
assert popup.styles.display == "block"
def ensure_selected_command(popup: CompletionPopup, expected_alias: str) -> None:

View file

@ -133,3 +133,13 @@ class TestCommandRegistry:
assert result is not None
_, cmd, _ = result
assert cmd.handler == "_show_data_retention"
def test_loop_command_registration(self) -> None:
registry = CommandRegistry()
assert registry.get_command_name("/loop") == "loop"
result = registry.parse_command("/loop 30s ping")
assert result is not None
cmd_name, cmd, cmd_args = result
assert cmd_name == "loop"
assert cmd.handler == "_loop_command"
assert cmd_args == "30s ping"

View file

@ -0,0 +1,28 @@
from __future__ import annotations
from unittest.mock import MagicMock
from vibe.cli.textual_ui.widgets.compact import CompactMessage
from vibe.core.session.session_id import shorten_session_id
class TestCompactMessage:
def test_get_content_includes_session_ids_after_compaction(self) -> None:
message = CompactMessage()
message.post_message = MagicMock()
message.set_complete(
old_tokens=177_017,
new_tokens=23_263,
old_session_id="11111111-1111-1111-1111-111111111111",
new_session_id="22222222-2222-2222-2222-222222222222",
)
assert message.get_content() == (
"Compaction complete: 177,017 → 23,263 tokens (-87.%)\n"
"session: "
f"{shorten_session_id('11111111-1111-1111-1111-111111111111')} "
"(before compaction) → "
f"{shorten_session_id('22222222-2222-2222-2222-222222222222')} "
"(after compaction)"
)

View file

@ -0,0 +1,55 @@
from __future__ import annotations
import argparse
from vibe.cli.cli import get_initial_agent_name
from vibe.core.agents.models import BuiltinAgentName
from vibe.core.config import VibeConfig
def _make_args(*, agent: str | None, prompt: str | None) -> argparse.Namespace:
return argparse.Namespace(agent=agent, prompt=prompt)
def test_uses_args_agent_when_provided() -> None:
config = VibeConfig.model_construct(default_agent=BuiltinAgentName.PLAN)
args = _make_args(agent="accept-edits", prompt=None)
assert get_initial_agent_name(args, config) == "accept-edits"
def test_falls_back_to_config_default_agent_when_args_agent_is_none() -> None:
config = VibeConfig.model_construct(default_agent=BuiltinAgentName.PLAN)
args = _make_args(agent=None, prompt=None)
assert get_initial_agent_name(args, config) == BuiltinAgentName.PLAN
def test_defaults_to_default_when_unset_in_config_and_args() -> None:
config = VibeConfig.model_construct()
args = _make_args(agent=None, prompt=None)
assert get_initial_agent_name(args, config) == BuiltinAgentName.DEFAULT
def test_programmatic_mode_promotes_default_to_auto_approve() -> None:
config = VibeConfig.model_construct(default_agent=BuiltinAgentName.DEFAULT)
args = _make_args(agent=None, prompt="hello")
assert get_initial_agent_name(args, config) == BuiltinAgentName.AUTO_APPROVE
def test_programmatic_mode_ignores_config_default_agent_when_args_agent_is_none() -> (
None
):
config = VibeConfig.model_construct(default_agent=BuiltinAgentName.PLAN)
args = _make_args(agent=None, prompt="hello")
assert get_initial_agent_name(args, config) == BuiltinAgentName.AUTO_APPROVE
def test_programmatic_mode_keeps_explicit_agent_arg() -> None:
config = VibeConfig.model_construct(default_agent=BuiltinAgentName.DEFAULT)
args = _make_args(agent="accept-edits", prompt="hello")
assert get_initial_agent_name(args, config) == "accept-edits"

View file

@ -1,6 +1,7 @@
from __future__ import annotations
import time
from typing import Any
from unittest.mock import AsyncMock, patch
import pytest
@ -10,7 +11,7 @@ from tests.conftest import build_test_vibe_app, build_test_vibe_config
from vibe.cli.plan_offer.ports.whoami_gateway import WhoAmIPlanType, WhoAmIResponse
from vibe.cli.textual_ui.widgets.chat_input import ChatInputContainer
from vibe.core.config import ModelConfig, ProviderConfig, VibeConfig
from vibe.core.types import Backend
from vibe.core.types import Backend, LLMMessage, Role
def _chat_plan_gateway(*, prompt_switching_to_pro_plan: bool) -> FakeWhoAmIGateway:
@ -36,6 +37,16 @@ async def _wait_until(pause, predicate, timeout: float = 2.0) -> None:
raise AssertionError("Condition was not met within the timeout")
def _teleport_failed_events(
telemetry_events: list[dict[str, Any]],
) -> list[dict[str, Any]]:
return [
event
for event in telemetry_events
if event["event_name"] == "vibe.teleport_failed"
]
@pytest.mark.asyncio
async def test_teleport_command_visible_for_paid_chat_users() -> None:
app = build_test_vibe_app(
@ -56,6 +67,81 @@ async def test_teleport_command_visible_for_paid_chat_users() -> None:
assert "&" in input_widget.mode_characters
@pytest.mark.asyncio
async def test_teleport_command_without_history_sends_early_failure_telemetry(
telemetry_events: list[dict[str, Any]],
) -> None:
app = build_test_vibe_app(
config=_vibe_code_enabled_config(),
plan_offer_gateway=_chat_plan_gateway(prompt_switching_to_pro_plan=False),
)
async with app.run_test() as pilot:
await _wait_until(
pilot.pause,
lambda: app.commands.get_command_name("/teleport") == "teleport",
)
await app.on_chat_input_container_submitted(
ChatInputContainer.Submitted("/teleport")
)
assert _teleport_failed_events(telemetry_events) == [
{
"event_name": "vibe.teleport_failed",
"properties": {
"stage": "no_history",
"error_class": "TeleportNoHistoryError",
"push_required": False,
"github_auth_required": False,
"nb_session_messages": 0,
"session_id": app.agent_loop.session_id,
},
}
]
@pytest.mark.asyncio
async def test_teleport_command_in_remote_session_sends_early_failure_telemetry(
telemetry_events: list[dict[str, Any]],
) -> None:
app = build_test_vibe_app(
config=_vibe_code_enabled_config(),
plan_offer_gateway=_chat_plan_gateway(prompt_switching_to_pro_plan=False),
)
app.agent_loop.messages.append(LLMMessage(role=Role.user, content="hello"))
app.agent_loop.messages.append(LLMMessage(role=Role.assistant, content="hi"))
async with app.run_test() as pilot:
await _wait_until(
pilot.pause,
lambda: app.commands.get_command_name("/teleport") == "teleport",
)
await app._remote_manager.attach(session_id="remote-session", config=app.config)
await app.on_chat_input_container_submitted(
ChatInputContainer.Submitted("/teleport")
)
await _wait_until(
pilot.pause, lambda: len(_teleport_failed_events(telemetry_events)) == 1
)
await app._remote_manager.detach()
assert _teleport_failed_events(telemetry_events) == [
{
"event_name": "vibe.teleport_failed",
"properties": {
"stage": "remote_session",
"error_class": "TeleportRemoteSessionError",
"push_required": False,
"github_auth_required": False,
"nb_session_messages": 2,
"session_id": app.agent_loop.session_id,
},
}
]
@pytest.mark.asyncio
async def test_teleport_command_hidden_when_current_key_is_not_eligible() -> None:
app = build_test_vibe_app(

View file

@ -53,6 +53,7 @@ def get_base_config() -> dict[str, Any]:
}
],
"enable_auto_update": False,
"enable_telemetry": False,
}
@ -174,7 +175,8 @@ def telemetry_events(monkeypatch: pytest.MonkeyPatch) -> list[dict[str, Any]]:
*,
correlation_id: str | None = None,
) -> None:
event: dict[str, Any] = {"event_name": event_name, "properties": properties}
merged = self.build_client_event_metadata() | properties
event: dict[str, Any] = {"event_name": event_name, "properties": merged}
if correlation_id is not None:
event["correlation_id"] = correlation_id
events.append(event)

View file

@ -104,3 +104,19 @@ class TestAgentManager:
)
manager = AgentManager(lambda: config, initial_agent="plan")
assert manager.active_profile.name == "plan"
def test_initial_agent_raises_when_agent_is_disabled(self) -> None:
config = build_test_vibe_config(
include_project_context=False,
include_prompt_detail=False,
disabled_agents=["plan"],
)
with pytest.raises(ValueError, match="not available"):
AgentManager(lambda: config, initial_agent="plan")
def test_initial_agent_raises_when_agent_does_not_exist(self) -> None:
config = build_test_vibe_config(
include_project_context=False, include_prompt_detail=False
)
with pytest.raises(ValueError, match="not found"):
AgentManager(lambda: config, initial_agent="nonexistent-agent")

304
tests/core/test_loop.py Normal file
View file

@ -0,0 +1,304 @@
from __future__ import annotations
import math
from typing import cast
import pytest
from vibe.core.loop import (
MAX_LOOPS_PER_SESSION,
MIN_INTERVAL_SECONDS,
LoopError,
LoopErrorResult,
LoopListResult,
LoopManager,
LoopOkResult,
ScheduledLoop,
format_duration,
parse_interval,
)
from vibe.core.session.session_logger import SessionLogger
class FakeMetadata:
def __init__(self) -> None:
self.loops: list[ScheduledLoop] = []
class FakeSessionLogger:
def __init__(self) -> None:
self.session_metadata = FakeMetadata()
self.persisted: list[list[ScheduledLoop]] = []
async def persist_loops(self) -> None:
self.persisted.append([*self.session_metadata.loops])
class RaisingSessionLogger:
def __init__(self) -> None:
self.session_metadata = FakeMetadata()
async def persist_loops(self) -> None:
raise RuntimeError("disk on fire")
def _build_manager() -> tuple[LoopManager, FakeSessionLogger]:
fake = FakeSessionLogger()
return LoopManager(cast(SessionLogger, fake)), fake
class TestParseInterval:
def test_parses_supported_units(self) -> None:
assert parse_interval("30s") == 30
assert parse_interval("5m") == 300
assert parse_interval("2h") == 7200
assert parse_interval("1d") == 86400
def test_parses_case_insensitively(self) -> None:
assert parse_interval("30S") == 30
assert parse_interval("2H") == 7200
def test_strips_whitespace(self) -> None:
assert parse_interval(" 30s ") == 30
@pytest.mark.parametrize("bad", ["", "5", "5x", "-5m", "5 m", "1.5m", "abc"])
def test_rejects_invalid_inputs(self, bad: str) -> None:
with pytest.raises(LoopError):
parse_interval(bad)
def test_rejects_below_minimum(self) -> None:
with pytest.raises(LoopError, match=f"at least {MIN_INTERVAL_SECONDS}"):
parse_interval("29s")
class TestFormatInterval:
@pytest.mark.parametrize("text", ["30s", "5m", "2h", "1d"])
def test_round_trip_with_parse(self, text: str) -> None:
assert format_duration(parse_interval(text)) == text
def test_formats_non_round_values(self) -> None:
assert format_duration(90) == "1m30s"
assert format_duration(180) == "3m"
assert format_duration(7200) == "2h"
assert format_duration(259200) == "3d"
def test_formats_short_version(self) -> None:
assert format_duration(90, short=True) == "1m"
assert format_duration(180, short=True) == "3m"
assert format_duration(190, short=True) == "3m"
assert format_duration(8000, short=True) == "2h"
assert format_duration(260000, short=True) == "3d"
class TestLoopManagerHandleCommand:
@pytest.mark.asyncio
async def test_empty_returns_loops_list(self) -> None:
manager, fake = _build_manager()
result = await manager.handle_command("")
assert isinstance(result, LoopListResult)
assert result.loops == []
assert fake.persisted == []
@pytest.mark.asyncio
@pytest.mark.parametrize("verb", ["list", "ls"])
async def test_list_alias_returns_loops_list(self, verb: str) -> None:
manager, fake = _build_manager()
result = await manager.handle_command(verb)
assert isinstance(result, LoopListResult)
assert result.loops == []
assert fake.persisted == []
@pytest.mark.asyncio
async def test_add_success_persists_and_returns_id(self) -> None:
manager, fake = _build_manager()
result = await manager.handle_command("1m hello world")
assert isinstance(result, LoopOkResult)
assert "every 1m" in result.message
assert "hello world" in result.message
assert len(manager.loops) == 1
loop = manager.loops[0]
assert loop.interval_seconds == 60
assert loop.prompt == "hello world"
assert result.message.count(loop.id) >= 1
assert len(fake.persisted) == 1
assert fake.persisted[0][0].id == loop.id
@pytest.mark.asyncio
async def test_add_bad_interval_returns_error_no_persist(self) -> None:
manager, fake = _build_manager()
result = await manager.handle_command("nope hello")
assert isinstance(result, LoopErrorResult)
assert "Invalid interval" in result.message
assert "Usage:" not in result.message
assert manager.loops == []
assert fake.persisted == []
@pytest.mark.asyncio
async def test_add_empty_prompt_returns_error_no_persist(self) -> None:
manager, fake = _build_manager()
result = await manager.handle_command("30s ")
assert isinstance(result, LoopErrorResult)
assert "Missing prompt" in result.message
assert manager.loops == []
assert fake.persisted == []
@pytest.mark.asyncio
async def test_add_prompt_starting_with_slash_returns_error(self) -> None:
manager, fake = _build_manager()
result = await manager.handle_command("30s /config")
assert isinstance(result, LoopErrorResult)
assert "cannot start with '/'" in result.message
assert manager.loops == []
assert fake.persisted == []
@pytest.mark.asyncio
async def test_over_limit_returns_error(self) -> None:
manager, _ = _build_manager()
for i in range(MAX_LOOPS_PER_SESSION):
result = await manager.handle_command(f"30s prompt {i}")
assert isinstance(result, LoopOkResult)
result = await manager.handle_command("30s overflow")
assert isinstance(result, LoopErrorResult)
assert "limit" in result.message.lower()
assert len(manager.loops) == MAX_LOOPS_PER_SESSION
@pytest.mark.asyncio
async def test_cancel_missing_target_is_error_no_persist(self) -> None:
manager, fake = _build_manager()
result = await manager.handle_command("cancel")
assert isinstance(result, LoopErrorResult)
assert "Missing loop id" in result.message
assert fake.persisted == []
@pytest.mark.asyncio
async def test_cancel_unknown_id_is_error_no_persist(self) -> None:
manager, fake = _build_manager()
await manager.handle_command("30s ping")
fake.persisted.clear()
result = await manager.handle_command("cancel deadbeef")
assert isinstance(result, LoopErrorResult)
assert "deadbeef" in result.message
assert fake.persisted == []
@pytest.mark.asyncio
async def test_cancel_known_id_persists(self) -> None:
manager, fake = _build_manager()
await manager.handle_command("30s ping")
loop_id = manager.loops[0].id
fake.persisted.clear()
result = await manager.handle_command(f"cancel {loop_id}")
assert isinstance(result, LoopOkResult)
assert loop_id in result.message
assert "ping" in result.message
assert manager.loops == []
assert fake.persisted == [[]]
@pytest.mark.asyncio
async def test_cancel_all_persists(self) -> None:
manager, fake = _build_manager()
await manager.handle_command("30s a")
await manager.handle_command("30s b")
fake.persisted.clear()
result = await manager.handle_command("cancel all")
assert isinstance(result, LoopOkResult)
assert "2 scheduled loop(s)" in result.message
assert manager.loops == []
assert fake.persisted == [[]]
@pytest.mark.asyncio
@pytest.mark.parametrize("verb", ["rm", "stop", "delete"])
async def test_cancel_alias_verbs_work(self, verb: str) -> None:
manager, _ = _build_manager()
await manager.handle_command("30s ping")
loop_id = manager.loops[0].id
result = await manager.handle_command(f"{verb} {loop_id}")
assert isinstance(result, LoopOkResult)
assert manager.loops == []
class TestLoopManagerPopDue:
@pytest.mark.asyncio
async def test_returns_none_when_empty(self) -> None:
manager, fake = _build_manager()
assert await manager.pop_due() is None
assert fake.persisted == []
@pytest.mark.asyncio
async def test_returns_none_when_nothing_due(self) -> None:
manager, fake = _build_manager()
await manager.handle_command("30s ping")
fake.persisted.clear()
result = await manager.pop_due(now=0.0)
assert result is None
assert fake.persisted == []
@pytest.mark.asyncio
async def test_returns_due_loop_advances_and_persists(self) -> None:
manager, fake = _build_manager()
await manager.handle_command("30s ping")
fake.persisted.clear()
loop = manager.loops[0]
original_next = loop.next_fire_at
due = await manager.pop_due(now=original_next + 100.0)
assert due is not None
assert due.id == loop.id
assert manager.loops[0].next_fire_at == original_next + 100.0 + 30.0
assert len(fake.persisted) == 1
class TestLoopManagerNextDueIn:
def test_inf_when_empty(self) -> None:
manager, _ = _build_manager()
assert manager.next_due_in() == math.inf
@pytest.mark.asyncio
async def test_correct_delta_when_populated(self) -> None:
manager, _ = _build_manager()
await manager.handle_command("30s ping")
loop = manager.loops[0]
delta = manager.next_due_in(now=loop.next_fire_at - 7.0)
assert delta == pytest.approx(7.0)
@pytest.mark.asyncio
async def test_zero_when_overdue(self) -> None:
manager, _ = _build_manager()
await manager.handle_command("30s ping")
loop = manager.loops[0]
delta = manager.next_due_in(now=loop.next_fire_at + 100.0)
assert delta == 0.0
class TestLoopManagerRestore:
def test_replaces_in_memory_list_without_persist(self) -> None:
manager, fake = _build_manager()
loops = [
ScheduledLoop(
id="aabbccdd",
interval_seconds=30,
prompt="x",
next_fire_at=1.0,
created_at=0.0,
),
ScheduledLoop(
id="11223344",
interval_seconds=60,
prompt="y",
next_fire_at=2.0,
created_at=0.0,
),
]
manager.restore(loops)
assert [loop.id for loop in manager.loops] == ["aabbccdd", "11223344"]
assert fake.persisted == []
class TestLoopManagerPersisterErrors:
@pytest.mark.asyncio
async def test_persister_exception_does_not_propagate(self) -> None:
manager = LoopManager(cast(SessionLogger, RaisingSessionLogger()))
result = await manager.handle_command("30s ping")
assert isinstance(result, LoopOkResult)
assert len(manager.loops) == 1
loop = manager.loops[0]
due = await manager.pop_due(now=loop.next_fire_at + 1.0)
assert due is not None

View file

@ -362,6 +362,48 @@ class TestTelemetryClient:
assert telemetry_events[1]["properties"]["command"] == "my_skill"
assert telemetry_events[1]["properties"]["command_type"] == "skill"
def test_send_teleport_completed_payload(
self, telemetry_events: list[dict[str, Any]]
) -> None:
config = build_test_vibe_config(enable_telemetry=True)
client = TelemetryClient(config_getter=lambda: config)
client.send_teleport_completed(
push_required=True, github_auth_required=False, nb_session_messages=4
)
assert len(telemetry_events) == 1
assert telemetry_events[0]["event_name"] == "vibe.teleport_completed"
assert telemetry_events[0]["properties"] == {
"push_required": True,
"github_auth_required": False,
"nb_session_messages": 4,
}
def test_send_teleport_failed_payload(
self, telemetry_events: list[dict[str, Any]]
) -> None:
config = build_test_vibe_config(enable_telemetry=True)
client = TelemetryClient(config_getter=lambda: config)
client.send_teleport_failed(
stage="push",
error_class="ServiceTeleportError",
push_required=True,
github_auth_required=False,
nb_session_messages=4,
)
assert len(telemetry_events) == 1
assert telemetry_events[0]["event_name"] == "vibe.teleport_failed"
assert telemetry_events[0]["properties"] == {
"stage": "push",
"error_class": "ServiceTeleportError",
"push_required": True,
"github_auth_required": False,
"nb_session_messages": 4,
}
def test_send_new_session_payload(
self, telemetry_events: list[dict[str, Any]]
) -> None:
@ -393,6 +435,55 @@ class TestTelemetryClient:
assert properties["terminal_emulator"] == "vscode"
assert "version" in properties
@pytest.mark.asyncio
async def test_send_session_closed_payload(
self, monkeypatch: pytest.MonkeyPatch
) -> None:
monkeypatch.setattr(
TelemetryClient, "send_telemetry_event", _original_send_telemetry_event
)
config = build_test_vibe_config(enable_telemetry=True)
env_key = config.get_active_provider().api_key_env_var
monkeypatch.setenv(env_key, "sk-test")
client = TelemetryClient(
config_getter=lambda: config,
session_id_getter=lambda: "current-session",
parent_session_id_getter=lambda: "current-parent-session",
entrypoint_metadata_getter=lambda: EntrypointMetadata(
agent_entrypoint="cli",
agent_version="1.0.0",
client_name="vibe_cli",
client_version="1.0.0",
),
)
mock_post = AsyncMock(return_value=MagicMock(status_code=204))
client._client = MagicMock()
client._client.post = mock_post
client._client.aclose = AsyncMock()
client.send_session_closed()
await client.aclose()
mock_post.assert_called_once_with(
"https://api.mistral.ai/v1/datalake/events",
json={
"event": "vibe.session_closed",
"properties": {
"agent_entrypoint": "cli",
"agent_version": "1.0.0",
"client_name": "vibe_cli",
"client_version": "1.0.0",
"session_id": "current-session",
"parent_session_id": "current-parent-session",
},
},
headers={
"Content-Type": "application/json",
"Authorization": "Bearer sk-test",
"User-Agent": get_user_agent(Backend.MISTRAL),
},
)
def test_build_base_metadata_includes_entrypoint_and_session(self) -> None:
metadata = build_base_metadata(
entrypoint_metadata=EntrypointMetadata(

View file

@ -0,0 +1,231 @@
from __future__ import annotations
import asyncio
from collections.abc import AsyncGenerator
from typing import Any, cast
import pytest
from vibe.core.agent_loop import AgentLoop, TeleportError
from vibe.core.teleport.errors import ServiceTeleportError
from vibe.core.teleport.teleport import TeleportService
from vibe.core.teleport.types import (
TeleportAuthRequiredEvent,
TeleportCheckingGitEvent,
TeleportCompleteEvent,
TeleportFetchingUrlEvent,
TeleportPushingEvent,
TeleportPushRequiredEvent,
TeleportPushResponseEvent,
TeleportStartingWorkflowEvent,
TeleportWaitingForGitHubEvent,
)
from vibe.core.types import LLMMessage, Role
def _set_teleport_service(agent_loop: AgentLoop, service: object) -> None:
agent_loop._teleport_service = cast(TeleportService, service)
class TestTeleportAgentLoopTelemetry:
@pytest.mark.asyncio
async def test_teleport_to_vibe_code_sends_completed_success(
self, agent_loop: AgentLoop, telemetry_events: list[dict[str, Any]]
) -> None:
class FakeTeleportService:
async def __aenter__(self) -> FakeTeleportService:
return self
async def __aexit__(self, *_args: object) -> None:
return None
async def execute(
self, *_args: object, **_kwargs: object
) -> AsyncGenerator[object, object]:
yield TeleportCheckingGitEvent()
yield TeleportPushRequiredEvent()
yield TeleportPushingEvent()
yield TeleportStartingWorkflowEvent()
yield TeleportWaitingForGitHubEvent()
yield TeleportAuthRequiredEvent(oauth_url="https://github.com/auth")
yield TeleportFetchingUrlEvent()
yield TeleportCompleteEvent(url="https://chat.example.com/123")
agent_loop.messages.append(LLMMessage(role=Role.user, content="hello"))
_set_teleport_service(agent_loop, FakeTeleportService())
gen = agent_loop.teleport_to_vibe_code(None)
response = None
events = []
while True:
try:
event = await gen.asend(response)
except StopAsyncIteration:
break
events.append(event)
response = (
TeleportPushResponseEvent(approved=True)
if isinstance(event, TeleportPushRequiredEvent)
else None
)
assert isinstance(events[-1], TeleportCompleteEvent)
assert telemetry_events[-1]["event_name"] == "vibe.teleport_completed"
assert telemetry_events[-1]["properties"] == {
"push_required": True,
"github_auth_required": True,
"nb_session_messages": 1,
"session_id": agent_loop.session_id,
}
@pytest.mark.asyncio
async def test_teleport_to_vibe_code_sends_failed_stage(
self, agent_loop: AgentLoop, telemetry_events: list[dict[str, Any]]
) -> None:
class FakeTeleportService:
async def __aenter__(self) -> FakeTeleportService:
return self
async def __aexit__(self, *_args: object) -> None:
return None
async def execute(
self, *_args: object, **_kwargs: object
) -> AsyncGenerator[object, object]:
yield TeleportCheckingGitEvent()
yield TeleportStartingWorkflowEvent()
raise ServiceTeleportError("Workflow api-key-123 could not be started.")
agent_loop.messages.append(LLMMessage(role=Role.user, content="hello"))
_set_teleport_service(agent_loop, FakeTeleportService())
gen = agent_loop.teleport_to_vibe_code(None)
with pytest.raises(TeleportError, match="api-key-123"):
async for _ in gen:
pass
assert telemetry_events[-1]["event_name"] == "vibe.teleport_failed"
assert telemetry_events[-1]["properties"] == {
"stage": "workflow_start",
"error_class": "ServiceTeleportError",
"push_required": False,
"github_auth_required": False,
"nb_session_messages": 1,
"session_id": agent_loop.session_id,
}
assert "api-key-123" not in str(telemetry_events[-1]["properties"])
@pytest.mark.asyncio
async def test_teleport_to_vibe_code_sends_failed_cancelled(
self, agent_loop: AgentLoop, telemetry_events: list[dict[str, Any]]
) -> None:
class FakeTeleportService:
async def __aenter__(self) -> FakeTeleportService:
return self
async def __aexit__(self, *_args: object) -> None:
return None
async def execute(
self, *_args: object, **_kwargs: object
) -> AsyncGenerator[object, object]:
yield TeleportCheckingGitEvent()
response = yield TeleportPushRequiredEvent()
if (
not isinstance(response, TeleportPushResponseEvent)
or not response.approved
):
raise ServiceTeleportError(
"Teleport cancelled: changes not pushed."
)
agent_loop.messages.append(LLMMessage(role=Role.user, content="hello"))
_set_teleport_service(agent_loop, FakeTeleportService())
gen = agent_loop.teleport_to_vibe_code(None)
assert isinstance(await gen.asend(None), TeleportCheckingGitEvent)
assert isinstance(await gen.asend(None), TeleportPushRequiredEvent)
with pytest.raises(TeleportError, match="Teleport cancelled"):
await gen.asend(TeleportPushResponseEvent(approved=False))
assert telemetry_events[-1]["event_name"] == "vibe.teleport_failed"
assert telemetry_events[-1]["properties"] == {
"stage": "cancelled",
"error_class": "ServiceTeleportError",
"push_required": True,
"github_auth_required": False,
"nb_session_messages": 1,
"session_id": agent_loop.session_id,
}
@pytest.mark.asyncio
async def test_teleport_to_vibe_code_sends_failed_when_task_cancelled(
self, agent_loop: AgentLoop, telemetry_events: list[dict[str, Any]]
) -> None:
class FakeTeleportService:
async def __aenter__(self) -> FakeTeleportService:
return self
async def __aexit__(self, *_args: object) -> None:
return None
async def execute(
self, *_args: object, **_kwargs: object
) -> AsyncGenerator[object, object]:
yield TeleportCheckingGitEvent()
raise asyncio.CancelledError
agent_loop.messages.append(LLMMessage(role=Role.user, content="hello"))
_set_teleport_service(agent_loop, FakeTeleportService())
gen = agent_loop.teleport_to_vibe_code(None)
assert isinstance(await gen.asend(None), TeleportCheckingGitEvent)
with pytest.raises(asyncio.CancelledError):
await gen.asend(None)
assert telemetry_events[-1]["event_name"] == "vibe.teleport_failed"
assert telemetry_events[-1]["properties"] == {
"stage": "cancelled",
"error_class": "CancelledError",
"push_required": False,
"github_auth_required": False,
"nb_session_messages": 1,
"session_id": agent_loop.session_id,
}
@pytest.mark.asyncio
async def test_teleport_to_vibe_code_sends_failed_when_consumer_closes_generator(
self, agent_loop: AgentLoop, telemetry_events: list[dict[str, Any]]
) -> None:
class FakeTeleportService:
async def __aenter__(self) -> FakeTeleportService:
return self
async def __aexit__(self, *_args: object) -> None:
return None
async def execute(
self, *_args: object, **_kwargs: object
) -> AsyncGenerator[object, object]:
yield TeleportCheckingGitEvent()
yield TeleportPushRequiredEvent()
agent_loop.messages.append(LLMMessage(role=Role.user, content="hello"))
_set_teleport_service(agent_loop, FakeTeleportService())
gen = agent_loop.teleport_to_vibe_code(None)
assert isinstance(await gen.asend(None), TeleportCheckingGitEvent)
await gen.aclose()
assert telemetry_events[-1]["event_name"] == "vibe.teleport_failed"
assert telemetry_events[-1]["properties"] == {
"stage": "cancelled",
"error_class": "CancelledError",
"push_required": False,
"github_auth_required": False,
"nb_session_messages": 1,
"session_id": agent_loop.session_id,
}

View file

@ -4,7 +4,7 @@ from pathlib import Path
import pytest
from vibe.core.utils import get_server_url_from_api_base
from vibe.core.utils import compact_reduction_display, get_server_url_from_api_base
import vibe.core.utils.io as io_utils
from vibe.core.utils.io import decode_safe, read_safe, read_safe_async
@ -23,6 +23,24 @@ def test_get_server_url_from_api_base(api_base, expected):
assert get_server_url_from_api_base(api_base) == expected
class TestCompactReductionDisplay:
def test_includes_session_ids_when_available(self) -> None:
assert compact_reduction_display(
177_017,
23_263,
old_session_id="11111111-1111-1111-1111-111111111111",
new_session_id="22222222-2222-2222-2222-222222222222",
) == (
"Compaction complete: 177,017 → 23,263 tokens (-87.%)\n"
"session: 11111111 (before compaction) → 22222222 (after compaction)"
)
def test_returns_base_message_without_session_ids(self) -> None:
assert compact_reduction_display(177_017, 23_263) == (
"Compaction complete: 177,017 → 23,263 tokens (-87.%)"
)
class TestReadSafe:
def test_reads_utf8(self, tmp_path: Path) -> None:
f = tmp_path / "hello.txt"

View file

@ -60,7 +60,62 @@ class TestReadFileExecution:
assert result.content == "line 50\nline 51\n"
assert result.lines_read == 2
assert result.was_truncated
@pytest.mark.asyncio
async def test_run_marks_truncated_when_max_read_bytes_exceeded(
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
monkeypatch.chdir(tmp_path)
test_file = tmp_path / "big.txt"
test_file.write_text(
"".join(f"line {i}\n" for i in range(100)), encoding="utf-8"
)
tool = ReadFile(
config_getter=lambda: ReadFileToolConfig(max_read_bytes=20),
state=ReadFileState(),
)
result = await collect_result(tool.run(ReadFileArgs(path=str(test_file))))
assert result.was_truncated
assert result.lines_read > 0
@pytest.mark.asyncio
async def test_run_not_truncated_when_eof_reached(
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
monkeypatch.chdir(tmp_path)
test_file = tmp_path / "small.txt"
test_file.write_text("a\nb\nc\n", encoding="utf-8")
tool = ReadFile(
config_getter=lambda: ReadFileToolConfig(max_read_bytes=1024),
state=ReadFileState(),
)
result = await collect_result(tool.run(ReadFileArgs(path=str(test_file))))
assert not result.was_truncated
assert result.lines_read == 3
@pytest.mark.asyncio
async def test_run_not_truncated_when_limit_matches_remaining_lines(
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
monkeypatch.chdir(tmp_path)
test_file = tmp_path / "exact.txt"
test_file.write_text("a\nb\nc\n", encoding="utf-8")
tool = ReadFile(
config_getter=lambda: ReadFileToolConfig(max_read_bytes=1024),
state=ReadFileState(),
)
result = await collect_result(
tool.run(ReadFileArgs(path=str(test_file), limit=10))
)
assert not result.was_truncated
assert result.lines_read == 3
class TestGetResultExtra:

View file

@ -5,33 +5,41 @@ from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from vibe.core.session.resume_sessions import (
SHORT_SESSION_ID_LEN,
list_remote_resume_sessions,
short_session_id,
)
from vibe.core.session.session_id import shorten_session_id
class TestShortenSessionId:
def test_shortens_to_first_8_chars(self) -> None:
sid = "abcdef1234567890"
assert shorten_session_id(sid) == "abcdef12"
def test_from_end_shortens_to_last_8_chars(self) -> None:
sid = "abcdef1234567890"
assert shorten_session_id(sid, from_end=True) == "34567890"
def test_returns_full_id_when_shorter_than_limit(self) -> None:
sid = "abc"
assert shorten_session_id(sid) == "abc"
assert shorten_session_id(sid, from_end=True) == "abc"
class TestShortSessionId:
def test_local_shortens_to_first_chars(self) -> None:
def test_local_delegates_to_shorten(self) -> None:
sid = "abcdef1234567890"
result = short_session_id(sid)
assert result == sid[:SHORT_SESSION_ID_LEN]
assert len(result) == SHORT_SESSION_ID_LEN
assert short_session_id(sid) == shorten_session_id(sid)
def test_local_is_default(self) -> None:
sid = "abcdef1234567890"
assert short_session_id(sid) == short_session_id(sid, source="local")
def test_remote_shortens_to_last_chars(self) -> None:
def test_remote_delegates_to_shorten_from_end(self) -> None:
sid = "abcdef1234567890"
result = short_session_id(sid, source="remote")
assert result == sid[-SHORT_SESSION_ID_LEN:]
assert len(result) == SHORT_SESSION_ID_LEN
def test_returns_full_id_when_shorter_than_limit(self) -> None:
sid = "abc"
assert short_session_id(sid) == "abc"
assert short_session_id(sid, source="remote") == "abc"
assert short_session_id(sid, source="remote") == shorten_session_id(
sid, from_end=True
)
def test_empty_string(self) -> None:
assert short_session_id("") == ""

View file

@ -11,6 +11,7 @@ import pytest
from tests.conftest import build_test_vibe_config
from vibe.core.agents.models import AgentProfile, AgentSafety
from vibe.core.config import SessionLoggingConfig, VibeConfig
from vibe.core.loop import ScheduledLoop
from vibe.core.session.session_logger import SessionLogger
from vibe.core.tools.manager import ToolManager
from vibe.core.types import AgentStats, LLMMessage, Role, SessionMetadata
@ -868,3 +869,136 @@ class TestSessionLoggerCleanupTmpFiles:
logger.maybe_cleanup_tmp_files()
assert cleanup_spy.call_count == 2
class TestPersistLoops:
@pytest.mark.asyncio
async def test_writes_into_existing_metadata(
self,
session_config: SessionLoggingConfig,
mock_vibe_config: VibeConfig,
mock_tool_manager: ToolManager,
mock_agent_profile: AgentProfile,
) -> None:
logger = SessionLogger(session_config, "test-session-loops")
await logger.save_interaction(
messages=[
LLMMessage(role=Role.system, content="System prompt"),
LLMMessage(role=Role.user, content="Hello"),
LLMMessage(role=Role.assistant, content="Hi there!"),
],
stats=AgentStats(steps=1),
base_config=mock_vibe_config,
tool_manager=mock_tool_manager,
agent_profile=mock_agent_profile,
)
assert logger.session_metadata is not None
logger.session_metadata.loops = [
ScheduledLoop(
id="aabbccdd",
interval_seconds=30,
prompt="ping",
next_fire_at=12345.0,
created_at=12000.0,
)
]
await logger.persist_loops()
assert logger.session_dir is not None
with open(logger.session_dir / "meta.json") as f:
metadata = json.load(f)
assert metadata["session_id"] == "test-session-loops"
assert metadata["total_messages"] == 2
assert metadata["loops"] == [
{
"id": "aabbccdd",
"interval_seconds": 30,
"prompt": "ping",
"next_fire_at": 12345.0,
"created_at": 12000.0,
}
]
@pytest.mark.asyncio
async def test_noop_when_metadata_file_missing(
self, session_config: SessionLoggingConfig
) -> None:
logger = SessionLogger(session_config, "no-meta-session")
assert logger.session_dir is not None
# No save_interaction was called -> meta.json does not exist
assert not (logger.session_dir / "meta.json").exists()
assert logger.session_metadata is not None
logger.session_metadata.loops = [
ScheduledLoop(
id="aabbccdd",
interval_seconds=30,
prompt="ping",
next_fire_at=1.0,
created_at=0.0,
)
]
await logger.persist_loops()
assert not (logger.session_dir / "meta.json").exists()
@pytest.mark.asyncio
async def test_noop_when_logging_disabled(
self, disabled_session_config: SessionLoggingConfig
) -> None:
logger = SessionLogger(disabled_session_config, "ignored")
# Should not raise even though session_metadata is None
await logger.persist_loops()
@pytest.mark.asyncio
async def test_subsequent_save_interaction_preserves_loops(
self,
session_config: SessionLoggingConfig,
mock_vibe_config: VibeConfig,
mock_tool_manager: ToolManager,
mock_agent_profile: AgentProfile,
) -> None:
logger = SessionLogger(session_config, "loops-vs-save")
messages = [
LLMMessage(role=Role.system, content="System prompt"),
LLMMessage(role=Role.user, content="Hello"),
LLMMessage(role=Role.assistant, content="Hi there!"),
]
await logger.save_interaction(
messages=messages,
stats=AgentStats(steps=1),
base_config=mock_vibe_config,
tool_manager=mock_tool_manager,
agent_profile=mock_agent_profile,
)
assert logger.session_metadata is not None
logger.session_metadata.loops = [
ScheduledLoop(
id="aabbccdd",
interval_seconds=30,
prompt="ping",
next_fire_at=12345.0,
created_at=12000.0,
)
]
await logger.persist_loops()
# A subsequent save (e.g. user sends another message) must not
# overwrite the on-disk loops with the stale in-memory value.
more_messages = [*messages, LLMMessage(role=Role.user, content="Again")]
await logger.save_interaction(
messages=more_messages,
stats=AgentStats(steps=2),
base_config=mock_vibe_config,
tool_manager=mock_tool_manager,
agent_profile=mock_agent_profile,
)
assert logger.session_dir is not None
with open(logger.session_dir / "meta.json") as f:
metadata = json.load(f)
assert len(metadata["loops"]) == 1
assert metadata["loops"][0]["id"] == "aabbccdd"

View file

@ -191,7 +191,7 @@
</text><text class="terminal-r1" x="1464" y="654.4" textLength="12.2" clip-path="url(#terminal-line-26)">
</text><text class="terminal-r1" x="0" y="678.8" textLength="268.4" clip-path="url(#terminal-line-27)">Hello!&#160;I&#160;can&#160;help&#160;you.</text><text class="terminal-r1" x="1464" y="678.8" textLength="12.2" clip-path="url(#terminal-line-27)">
</text><text class="terminal-r1" x="1464" y="703.2" textLength="12.2" clip-path="url(#terminal-line-28)">
</text><text class="terminal-r5" x="12.2" y="727.6" textLength="36.6" clip-path="url(#terminal-line-29)">▂▅▇</text><text class="terminal-r1" x="48.8" y="727.6" textLength="122" clip-path="url(#terminal-line-29)">&#160;speaking&#160;</text><text class="terminal-r6" x="170.8" y="727.6" textLength="219.6" clip-path="url(#terminal-line-29)">Esc/Ctrl+C&#160;to&#160;stop</text><text class="terminal-r1" x="1464" y="727.6" textLength="12.2" clip-path="url(#terminal-line-29)">
</text><text class="terminal-r5" x="0" y="727.6" textLength="36.6" clip-path="url(#terminal-line-29)">▂▅▇</text><text class="terminal-r1" x="36.6" y="727.6" textLength="122" clip-path="url(#terminal-line-29)">&#160;speaking&#160;</text><text class="terminal-r6" x="158.6" y="727.6" textLength="219.6" clip-path="url(#terminal-line-29)">Esc/Ctrl+C&#160;to&#160;stop</text><text class="terminal-r1" x="1464" y="727.6" textLength="12.2" clip-path="url(#terminal-line-29)">
</text><text class="terminal-r7" x="0" y="752" textLength="1464" clip-path="url(#terminal-line-30)">┌────────────────────────────────────────────────────────────────────────────────────────────────────────────&#160;default&#160;─┐</text><text class="terminal-r1" x="1464" y="752" textLength="12.2" clip-path="url(#terminal-line-30)">
</text><text class="terminal-r7" x="0" y="776.4" textLength="12.2" clip-path="url(#terminal-line-31)"></text><text class="terminal-r2" x="24.4" y="776.4" textLength="12.2" clip-path="url(#terminal-line-31)">&gt;</text><text class="terminal-r7" x="1451.8" y="776.4" textLength="12.2" clip-path="url(#terminal-line-31)"></text><text class="terminal-r1" x="1464" y="776.4" textLength="12.2" clip-path="url(#terminal-line-31)">
</text><text class="terminal-r7" x="0" y="800.8" textLength="12.2" clip-path="url(#terminal-line-32)"></text><text class="terminal-r7" x="1451.8" y="800.8" textLength="12.2" clip-path="url(#terminal-line-32)"></text><text class="terminal-r1" x="1464" y="800.8" textLength="12.2" clip-path="url(#terminal-line-32)">

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Before After
Before After

View file

@ -191,7 +191,7 @@
</text><text class="terminal-r1" x="1464" y="654.4" textLength="12.2" clip-path="url(#terminal-line-26)">
</text><text class="terminal-r1" x="0" y="678.8" textLength="268.4" clip-path="url(#terminal-line-27)">Hello!&#160;I&#160;can&#160;help&#160;you.</text><text class="terminal-r1" x="1464" y="678.8" textLength="12.2" clip-path="url(#terminal-line-27)">
</text><text class="terminal-r1" x="1464" y="703.2" textLength="12.2" clip-path="url(#terminal-line-28)">
</text><text class="terminal-r5" x="12.2" y="727.6" textLength="12.2" clip-path="url(#terminal-line-29)"></text><text class="terminal-r1" x="24.4" y="727.6" textLength="158.6" clip-path="url(#terminal-line-29)">&#160;summarizing&#160;</text><text class="terminal-r6" x="183" y="727.6" textLength="219.6" clip-path="url(#terminal-line-29)">Esc/Ctrl+C&#160;to&#160;stop</text><text class="terminal-r1" x="1464" y="727.6" textLength="12.2" clip-path="url(#terminal-line-29)">
</text><text class="terminal-r5" x="0" y="727.6" textLength="12.2" clip-path="url(#terminal-line-29)"></text><text class="terminal-r1" x="12.2" y="727.6" textLength="158.6" clip-path="url(#terminal-line-29)">&#160;summarizing&#160;</text><text class="terminal-r6" x="170.8" y="727.6" textLength="219.6" clip-path="url(#terminal-line-29)">Esc/Ctrl+C&#160;to&#160;stop</text><text class="terminal-r1" x="1464" y="727.6" textLength="12.2" clip-path="url(#terminal-line-29)">
</text><text class="terminal-r7" x="0" y="752" textLength="1464" clip-path="url(#terminal-line-30)">┌────────────────────────────────────────────────────────────────────────────────────────────────────────────&#160;default&#160;─┐</text><text class="terminal-r1" x="1464" y="752" textLength="12.2" clip-path="url(#terminal-line-30)">
</text><text class="terminal-r7" x="0" y="776.4" textLength="12.2" clip-path="url(#terminal-line-31)"></text><text class="terminal-r2" x="24.4" y="776.4" textLength="12.2" clip-path="url(#terminal-line-31)">&gt;</text><text class="terminal-r7" x="1451.8" y="776.4" textLength="12.2" clip-path="url(#terminal-line-31)"></text><text class="terminal-r1" x="1464" y="776.4" textLength="12.2" clip-path="url(#terminal-line-31)">
</text><text class="terminal-r7" x="0" y="800.8" textLength="12.2" clip-path="url(#terminal-line-32)"></text><text class="terminal-r7" x="1451.8" y="800.8" textLength="12.2" clip-path="url(#terminal-line-32)"></text><text class="terminal-r1" x="1464" y="800.8" textLength="12.2" clip-path="url(#terminal-line-32)">

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Before After
Before After

View file

@ -33,13 +33,12 @@
}
.terminal-r1 { fill: #c5c8c6 }
.terminal-r2 { fill: #68a0b3 }
.terminal-r3 { fill: #4b4e55 }
.terminal-r4 { fill: #9a9b99 }
.terminal-r5 { fill: #608ab1;font-weight: bold }
.terminal-r2 { fill: #9a9b99 }
.terminal-r3 { fill: #608ab1;font-weight: bold }
.terminal-r4 { fill: #98a84b;font-weight: bold }
.terminal-r5 { fill: #4b4e55 }
.terminal-r6 { fill: #292929 }
.terminal-r7 { fill: #98a84b;font-weight: bold }
.terminal-r8 { fill: #ff8205;font-weight: bold }
.terminal-r7 { fill: #ff8205;font-weight: bold }
</style>
<defs>
@ -197,56 +196,56 @@
</g>
<g transform="translate(9, 41)" clip-path="url(#terminal-clip-terminal)">
<rect fill="#4b4e55" x="1451.8" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="221.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="245.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="269.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="294.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="318.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="343.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="367.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="391.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="416.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="440.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="465.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="489.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="513.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="538.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="562.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="587.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="611.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="635.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="660.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="684.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="709.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="733.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="757.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="782.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="806.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="831.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="855.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="879.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="904.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="928.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="953.1" width="12.2" height="24.65" shape-rendering="crispEdges"/>
<rect fill="#4b4e55" x="1451.8" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="221.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="245.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="269.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="294.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="318.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="343.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="367.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="391.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="416.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="440.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="465.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="489.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="513.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="538.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="562.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="587.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="611.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="635.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="660.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="684.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="709.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="733.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="757.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="782.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="806.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="831.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="855.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="879.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="904.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="928.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="953.1" width="12.2" height="24.65" shape-rendering="crispEdges"/>
<g class="terminal-matrix">
<text class="terminal-r1" x="0" y="20" textLength="134.2" clip-path="url(#terminal-line-0)">&#160;&#160;⠉⠒⠣⠤⠵⠤⠬⠮⠆</text><text class="terminal-r1" x="170.8" y="20" textLength="61" clip-path="url(#terminal-line-0)">Type&#160;</text><text class="terminal-r2" x="231.8" y="20" textLength="61" clip-path="url(#terminal-line-0)">/help</text><text class="terminal-r1" x="292.8" y="20" textLength="256.2" clip-path="url(#terminal-line-0)">&#160;for&#160;more&#160;information</text><text class="terminal-r1" x="1464" y="20" textLength="12.2" clip-path="url(#terminal-line-0)">
</text><text class="terminal-r3" x="1451.8" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)"></text><text class="terminal-r1" x="1464" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">
</text><text class="terminal-r4" x="24.4" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)"></text><text class="terminal-r5" x="48.8" y="68.8" textLength="219.6" clip-path="url(#terminal-line-2)">Keyboard&#160;Shortcuts</text><text class="terminal-r1" x="1464" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">
</text><text class="terminal-r4" x="24.4" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)"></text><text class="terminal-r1" x="48.8" y="93.2" textLength="24.4" clip-path="url(#terminal-line-3)">&#160;</text><text class="terminal-r7" x="73.2" y="93.2" textLength="61" clip-path="url(#terminal-line-3)">Enter</text><text class="terminal-r1" x="134.2" y="93.2" textLength="183" clip-path="url(#terminal-line-3)">&#160;Submit&#160;message</text><text class="terminal-r1" x="1464" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">
</text><text class="terminal-r4" x="24.4" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)"></text><text class="terminal-r1" x="48.8" y="117.6" textLength="24.4" clip-path="url(#terminal-line-4)">&#160;</text><text class="terminal-r7" x="73.2" y="117.6" textLength="73.2" clip-path="url(#terminal-line-4)">Ctrl+J</text><text class="terminal-r1" x="146.4" y="117.6" textLength="36.6" clip-path="url(#terminal-line-4)">&#160;/&#160;</text><text class="terminal-r7" x="183" y="117.6" textLength="134.2" clip-path="url(#terminal-line-4)">Shift+Enter</text><text class="terminal-r1" x="317.2" y="117.6" textLength="183" clip-path="url(#terminal-line-4)">&#160;Insert&#160;newline</text><text class="terminal-r1" x="1464" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">
</text><text class="terminal-r4" x="24.4" y="142" textLength="12.2" clip-path="url(#terminal-line-5)"></text><text class="terminal-r1" x="48.8" y="142" textLength="24.4" clip-path="url(#terminal-line-5)">&#160;</text><text class="terminal-r7" x="73.2" y="142" textLength="73.2" clip-path="url(#terminal-line-5)">Escape</text><text class="terminal-r1" x="146.4" y="142" textLength="402.6" clip-path="url(#terminal-line-5)">&#160;Interrupt&#160;agent&#160;or&#160;close&#160;dialogs</text><text class="terminal-r1" x="1464" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">
</text><text class="terminal-r4" x="24.4" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)"></text><text class="terminal-r1" x="48.8" y="166.4" textLength="24.4" clip-path="url(#terminal-line-6)">&#160;</text><text class="terminal-r7" x="73.2" y="166.4" textLength="73.2" clip-path="url(#terminal-line-6)">Ctrl+C</text><text class="terminal-r1" x="146.4" y="166.4" textLength="463.6" clip-path="url(#terminal-line-6)">&#160;Quit&#160;(or&#160;clear&#160;input&#160;if&#160;text&#160;present)</text><text class="terminal-r1" x="1464" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">
</text><text class="terminal-r4" x="24.4" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)"></text><text class="terminal-r1" x="48.8" y="190.8" textLength="24.4" clip-path="url(#terminal-line-7)">&#160;</text><text class="terminal-r7" x="73.2" y="190.8" textLength="73.2" clip-path="url(#terminal-line-7)">Ctrl+G</text><text class="terminal-r1" x="146.4" y="190.8" textLength="366" clip-path="url(#terminal-line-7)">&#160;Edit&#160;input&#160;in&#160;external&#160;editor</text><text class="terminal-r1" x="1464" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">
</text><text class="terminal-r4" x="24.4" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)"></text><text class="terminal-r1" x="48.8" y="215.2" textLength="24.4" clip-path="url(#terminal-line-8)">&#160;</text><text class="terminal-r7" x="73.2" y="215.2" textLength="73.2" clip-path="url(#terminal-line-8)">Ctrl+O</text><text class="terminal-r1" x="146.4" y="215.2" textLength="292.8" clip-path="url(#terminal-line-8)">&#160;Toggle&#160;tool&#160;output&#160;view</text><text class="terminal-r1" x="1464" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">
</text><text class="terminal-r4" x="24.4" y="239.6" textLength="12.2" clip-path="url(#terminal-line-9)"></text><text class="terminal-r1" x="48.8" y="239.6" textLength="24.4" clip-path="url(#terminal-line-9)">&#160;</text><text class="terminal-r7" x="73.2" y="239.6" textLength="109.8" clip-path="url(#terminal-line-9)">Shift+Tab</text><text class="terminal-r1" x="183" y="239.6" textLength="512.4" clip-path="url(#terminal-line-9)">&#160;Cycle&#160;through&#160;agents&#160;(default,&#160;plan,&#160;...)</text><text class="terminal-r1" x="1464" y="239.6" textLength="12.2" clip-path="url(#terminal-line-9)">
</text><text class="terminal-r4" x="24.4" y="264" textLength="12.2" clip-path="url(#terminal-line-10)"></text><text class="terminal-r1" x="48.8" y="264" textLength="24.4" clip-path="url(#terminal-line-10)">&#160;</text><text class="terminal-r7" x="73.2" y="264" textLength="73.2" clip-path="url(#terminal-line-10)">Alt+↑↓</text><text class="terminal-r1" x="146.4" y="264" textLength="36.6" clip-path="url(#terminal-line-10)">&#160;/&#160;</text><text class="terminal-r7" x="183" y="264" textLength="97.6" clip-path="url(#terminal-line-10)">Ctrl+P/N</text><text class="terminal-r1" x="280.6" y="264" textLength="390.4" clip-path="url(#terminal-line-10)">&#160;Rewind&#160;to&#160;previous/next&#160;message</text><text class="terminal-r1" x="1464" y="264" textLength="12.2" clip-path="url(#terminal-line-10)">
</text><text class="terminal-r4" x="24.4" y="288.4" textLength="12.2" clip-path="url(#terminal-line-11)"></text><text class="terminal-r1" x="1464" y="288.4" textLength="12.2" clip-path="url(#terminal-line-11)">
</text><text class="terminal-r4" x="24.4" y="312.8" textLength="12.2" clip-path="url(#terminal-line-12)"></text><text class="terminal-r5" x="48.8" y="312.8" textLength="195.2" clip-path="url(#terminal-line-12)">Special&#160;Features</text><text class="terminal-r1" x="1464" y="312.8" textLength="12.2" clip-path="url(#terminal-line-12)">
</text><text class="terminal-r4" x="24.4" y="337.2" textLength="12.2" clip-path="url(#terminal-line-13)"></text><text class="terminal-r1" x="1464" y="337.2" textLength="12.2" clip-path="url(#terminal-line-13)">
</text><text class="terminal-r4" x="24.4" y="361.6" textLength="12.2" clip-path="url(#terminal-line-14)"></text><text class="terminal-r1" x="48.8" y="361.6" textLength="24.4" clip-path="url(#terminal-line-14)">&#160;</text><text class="terminal-r7" x="73.2" y="361.6" textLength="122" clip-path="url(#terminal-line-14)">!&lt;command&gt;</text><text class="terminal-r1" x="195.2" y="361.6" textLength="366" clip-path="url(#terminal-line-14)">&#160;Execute&#160;bash&#160;command&#160;directly</text><text class="terminal-r1" x="1464" y="361.6" textLength="12.2" clip-path="url(#terminal-line-14)">
</text><text class="terminal-r4" x="24.4" y="386" textLength="12.2" clip-path="url(#terminal-line-15)"></text><text class="terminal-r1" x="48.8" y="386" textLength="24.4" clip-path="url(#terminal-line-15)">&#160;</text><text class="terminal-r7" x="73.2" y="386" textLength="170.8" clip-path="url(#terminal-line-15)">@path/to/file/</text><text class="terminal-r1" x="244" y="386" textLength="305" clip-path="url(#terminal-line-15)">&#160;Autocompletes&#160;file&#160;paths</text><text class="terminal-r1" x="1464" y="386" textLength="12.2" clip-path="url(#terminal-line-15)">
</text><text class="terminal-r4" x="24.4" y="410.4" textLength="12.2" clip-path="url(#terminal-line-16)"></text><text class="terminal-r1" x="1464" y="410.4" textLength="12.2" clip-path="url(#terminal-line-16)">
</text><text class="terminal-r4" x="24.4" y="434.8" textLength="12.2" clip-path="url(#terminal-line-17)"></text><text class="terminal-r5" x="48.8" y="434.8" textLength="97.6" clip-path="url(#terminal-line-17)">Commands</text><text class="terminal-r1" x="1464" y="434.8" textLength="12.2" clip-path="url(#terminal-line-17)">
</text><text class="terminal-r4" x="24.4" y="459.2" textLength="12.2" clip-path="url(#terminal-line-18)"></text><text class="terminal-r1" x="1464" y="459.2" textLength="12.2" clip-path="url(#terminal-line-18)">
</text><text class="terminal-r4" x="24.4" y="483.6" textLength="12.2" clip-path="url(#terminal-line-19)"></text><text class="terminal-r1" x="48.8" y="483.6" textLength="24.4" clip-path="url(#terminal-line-19)">&#160;</text><text class="terminal-r7" x="73.2" y="483.6" textLength="61" clip-path="url(#terminal-line-19)">/help</text><text class="terminal-r1" x="134.2" y="483.6" textLength="231.8" clip-path="url(#terminal-line-19)">:&#160;Show&#160;help&#160;message</text><text class="terminal-r1" x="1464" y="483.6" textLength="12.2" clip-path="url(#terminal-line-19)">
</text><text class="terminal-r4" x="24.4" y="508" textLength="12.2" clip-path="url(#terminal-line-20)"></text><text class="terminal-r1" x="48.8" y="508" textLength="24.4" clip-path="url(#terminal-line-20)">&#160;</text><text class="terminal-r7" x="73.2" y="508" textLength="85.4" clip-path="url(#terminal-line-20)">/config</text><text class="terminal-r1" x="158.6" y="508" textLength="268.4" clip-path="url(#terminal-line-20)">:&#160;Edit&#160;config&#160;settings</text><text class="terminal-r1" x="1464" y="508" textLength="12.2" clip-path="url(#terminal-line-20)">
</text><text class="terminal-r4" x="24.4" y="532.4" textLength="12.2" clip-path="url(#terminal-line-21)"></text><text class="terminal-r1" x="48.8" y="532.4" textLength="24.4" clip-path="url(#terminal-line-21)">&#160;</text><text class="terminal-r7" x="73.2" y="532.4" textLength="73.2" clip-path="url(#terminal-line-21)">/model</text><text class="terminal-r1" x="146.4" y="532.4" textLength="256.2" clip-path="url(#terminal-line-21)">:&#160;Select&#160;active&#160;model</text><text class="terminal-r1" x="1464" y="532.4" textLength="12.2" clip-path="url(#terminal-line-21)">
</text><text class="terminal-r4" x="24.4" y="556.8" textLength="12.2" clip-path="url(#terminal-line-22)"></text><text class="terminal-r1" x="48.8" y="556.8" textLength="24.4" clip-path="url(#terminal-line-22)">&#160;</text><text class="terminal-r7" x="73.2" y="556.8" textLength="109.8" clip-path="url(#terminal-line-22)">/thinking</text><text class="terminal-r1" x="183" y="556.8" textLength="280.6" clip-path="url(#terminal-line-22)">:&#160;Select&#160;thinking&#160;level</text><text class="terminal-r1" x="1464" y="556.8" textLength="12.2" clip-path="url(#terminal-line-22)">
</text><text class="terminal-r4" x="24.4" y="581.2" textLength="12.2" clip-path="url(#terminal-line-23)"></text><text class="terminal-r1" x="48.8" y="581.2" textLength="24.4" clip-path="url(#terminal-line-23)">&#160;</text><text class="terminal-r7" x="73.2" y="581.2" textLength="85.4" clip-path="url(#terminal-line-23)">/reload</text><text class="terminal-r1" x="158.6" y="581.2" textLength="780.8" clip-path="url(#terminal-line-23)">:&#160;Reload&#160;configuration,&#160;agent&#160;instructions,&#160;and&#160;skills&#160;from&#160;disk</text><text class="terminal-r1" x="1464" y="581.2" textLength="12.2" clip-path="url(#terminal-line-23)">
</text><text class="terminal-r4" x="24.4" y="605.6" textLength="12.2" clip-path="url(#terminal-line-24)"></text><text class="terminal-r1" x="48.8" y="605.6" textLength="24.4" clip-path="url(#terminal-line-24)">&#160;</text><text class="terminal-r7" x="73.2" y="605.6" textLength="73.2" clip-path="url(#terminal-line-24)">/clear</text><text class="terminal-r1" x="146.4" y="605.6" textLength="341.6" clip-path="url(#terminal-line-24)">:&#160;Clear&#160;conversation&#160;history</text><text class="terminal-r1" x="1464" y="605.6" textLength="12.2" clip-path="url(#terminal-line-24)">
</text><text class="terminal-r4" x="24.4" y="630" textLength="12.2" clip-path="url(#terminal-line-25)"></text><text class="terminal-r1" x="48.8" y="630" textLength="24.4" clip-path="url(#terminal-line-25)">&#160;</text><text class="terminal-r7" x="73.2" y="630" textLength="61" clip-path="url(#terminal-line-25)">/copy</text><text class="terminal-r1" x="134.2" y="630" textLength="561.2" clip-path="url(#terminal-line-25)">:&#160;Copy&#160;the&#160;last&#160;agent&#160;message&#160;to&#160;the&#160;clipboard</text><text class="terminal-r1" x="1464" y="630" textLength="12.2" clip-path="url(#terminal-line-25)">
</text><text class="terminal-r4" x="24.4" y="654.4" textLength="12.2" clip-path="url(#terminal-line-26)"></text><text class="terminal-r1" x="48.8" y="654.4" textLength="24.4" clip-path="url(#terminal-line-26)">&#160;</text><text class="terminal-r7" x="73.2" y="654.4" textLength="48.8" clip-path="url(#terminal-line-26)">/log</text><text class="terminal-r1" x="122" y="654.4" textLength="524.6" clip-path="url(#terminal-line-26)">:&#160;Show&#160;path&#160;to&#160;current&#160;interaction&#160;log&#160;file</text><text class="terminal-r1" x="1464" y="654.4" textLength="12.2" clip-path="url(#terminal-line-26)">
</text><text class="terminal-r4" x="24.4" y="678.8" textLength="12.2" clip-path="url(#terminal-line-27)"></text><text class="terminal-r1" x="48.8" y="678.8" textLength="24.4" clip-path="url(#terminal-line-27)">&#160;</text><text class="terminal-r7" x="73.2" y="678.8" textLength="73.2" clip-path="url(#terminal-line-27)">/debug</text><text class="terminal-r1" x="146.4" y="678.8" textLength="268.4" clip-path="url(#terminal-line-27)">:&#160;Toggle&#160;debug&#160;console</text><text class="terminal-r1" x="1464" y="678.8" textLength="12.2" clip-path="url(#terminal-line-27)">
</text><text class="terminal-r4" x="24.4" y="703.2" textLength="12.2" clip-path="url(#terminal-line-28)"></text><text class="terminal-r1" x="48.8" y="703.2" textLength="24.4" clip-path="url(#terminal-line-28)">&#160;</text><text class="terminal-r7" x="73.2" y="703.2" textLength="97.6" clip-path="url(#terminal-line-28)">/compact</text><text class="terminal-r1" x="170.8" y="703.2" textLength="1171.2" clip-path="url(#terminal-line-28)">:&#160;Compact&#160;conversation&#160;history&#160;by&#160;summarizing.&#160;Optionally&#160;pass&#160;instructions&#160;to&#160;guide&#160;the&#160;summary</text><text class="terminal-r1" x="1464" y="703.2" textLength="12.2" clip-path="url(#terminal-line-28)">
</text><text class="terminal-r4" x="24.4" y="727.6" textLength="12.2" clip-path="url(#terminal-line-29)"></text><text class="terminal-r1" x="48.8" y="727.6" textLength="24.4" clip-path="url(#terminal-line-29)">&#160;</text><text class="terminal-r7" x="73.2" y="727.6" textLength="61" clip-path="url(#terminal-line-29)">/exit</text><text class="terminal-r1" x="134.2" y="727.6" textLength="268.4" clip-path="url(#terminal-line-29)">:&#160;Exit&#160;the&#160;application</text><text class="terminal-r1" x="1464" y="727.6" textLength="12.2" clip-path="url(#terminal-line-29)">
</text><text class="terminal-r4" x="24.4" y="752" textLength="12.2" clip-path="url(#terminal-line-30)"></text><text class="terminal-r1" x="48.8" y="752" textLength="24.4" clip-path="url(#terminal-line-30)">&#160;</text><text class="terminal-r7" x="73.2" y="752" textLength="85.4" clip-path="url(#terminal-line-30)">/status</text><text class="terminal-r1" x="158.6" y="752" textLength="317.2" clip-path="url(#terminal-line-30)">:&#160;Display&#160;agent&#160;statistics</text><text class="terminal-r1" x="1464" y="752" textLength="12.2" clip-path="url(#terminal-line-30)">
</text><text class="terminal-r4" x="24.4" y="776.4" textLength="12.2" clip-path="url(#terminal-line-31)"></text><text class="terminal-r1" x="48.8" y="776.4" textLength="24.4" clip-path="url(#terminal-line-31)">&#160;</text><text class="terminal-r7" x="73.2" y="776.4" textLength="146.4" clip-path="url(#terminal-line-31)">/proxy-setup</text><text class="terminal-r1" x="219.6" y="776.4" textLength="561.2" clip-path="url(#terminal-line-31)">:&#160;Configure&#160;proxy&#160;and&#160;SSL&#160;certificate&#160;settings</text><text class="terminal-r1" x="1464" y="776.4" textLength="12.2" clip-path="url(#terminal-line-31)">
</text><text class="terminal-r4" x="24.4" y="800.8" textLength="12.2" clip-path="url(#terminal-line-32)"></text><text class="terminal-r1" x="48.8" y="800.8" textLength="24.4" clip-path="url(#terminal-line-32)">&#160;</text><text class="terminal-r7" x="73.2" y="800.8" textLength="109.8" clip-path="url(#terminal-line-32)">/continue</text><text class="terminal-r1" x="183" y="800.8" textLength="24.4" clip-path="url(#terminal-line-32)">,&#160;</text><text class="terminal-r7" x="207.4" y="800.8" textLength="85.4" clip-path="url(#terminal-line-32)">/resume</text><text class="terminal-r1" x="292.8" y="800.8" textLength="402.6" clip-path="url(#terminal-line-32)">:&#160;Browse&#160;and&#160;resume&#160;past&#160;sessions</text><text class="terminal-r1" x="1464" y="800.8" textLength="12.2" clip-path="url(#terminal-line-32)">
</text><text class="terminal-r4" x="24.4" y="825.2" textLength="12.2" clip-path="url(#terminal-line-33)"></text><text class="terminal-r1" x="48.8" y="825.2" textLength="24.4" clip-path="url(#terminal-line-33)">&#160;</text><text class="terminal-r7" x="73.2" y="825.2" textLength="85.4" clip-path="url(#terminal-line-33)">/rename</text><text class="terminal-r1" x="158.6" y="825.2" textLength="341.6" clip-path="url(#terminal-line-33)">:&#160;Rename&#160;the&#160;current&#160;session</text><text class="terminal-r1" x="1464" y="825.2" textLength="12.2" clip-path="url(#terminal-line-33)">
</text><text class="terminal-r4" x="24.4" y="849.6" textLength="12.2" clip-path="url(#terminal-line-34)"></text><text class="terminal-r1" x="48.8" y="849.6" textLength="24.4" clip-path="url(#terminal-line-34)">&#160;</text><text class="terminal-r7" x="73.2" y="849.6" textLength="134.2" clip-path="url(#terminal-line-34)">/connectors</text><text class="terminal-r1" x="207.4" y="849.6" textLength="24.4" clip-path="url(#terminal-line-34)">,&#160;</text><text class="terminal-r7" x="231.8" y="849.6" textLength="48.8" clip-path="url(#terminal-line-34)">/mcp</text><text class="terminal-r1" x="280.6" y="849.6" textLength="939.4" clip-path="url(#terminal-line-34)">:&#160;Display&#160;available&#160;MCP&#160;servers&#160;and&#160;connectors.&#160;Pass&#160;a&#160;name&#160;to&#160;list&#160;its&#160;tools</text><text class="terminal-r1" x="1464" y="849.6" textLength="12.2" clip-path="url(#terminal-line-34)">
</text><text class="terminal-r4" x="24.4" y="874" textLength="12.2" clip-path="url(#terminal-line-35)"></text><text class="terminal-r1" x="48.8" y="874" textLength="24.4" clip-path="url(#terminal-line-35)">&#160;</text><text class="terminal-r7" x="73.2" y="874" textLength="73.2" clip-path="url(#terminal-line-35)">/voice</text><text class="terminal-r1" x="146.4" y="874" textLength="317.2" clip-path="url(#terminal-line-35)">:&#160;Configure&#160;voice&#160;settings</text><text class="terminal-r1" x="1464" y="874" textLength="12.2" clip-path="url(#terminal-line-35)">
</text><text class="terminal-r4" x="24.4" y="898.4" textLength="12.2" clip-path="url(#terminal-line-36)"></text><text class="terminal-r1" x="48.8" y="898.4" textLength="24.4" clip-path="url(#terminal-line-36)">&#160;</text><text class="terminal-r7" x="73.2" y="898.4" textLength="122" clip-path="url(#terminal-line-36)">/leanstall</text><text class="terminal-r1" x="195.2" y="898.4" textLength="463.6" clip-path="url(#terminal-line-36)">:&#160;Install&#160;the&#160;Lean&#160;4&#160;agent&#160;(leanstral)</text><text class="terminal-r1" x="1464" y="898.4" textLength="12.2" clip-path="url(#terminal-line-36)">
</text><text class="terminal-r4" x="24.4" y="922.8" textLength="12.2" clip-path="url(#terminal-line-37)"></text><text class="terminal-r1" x="48.8" y="922.8" textLength="24.4" clip-path="url(#terminal-line-37)">&#160;</text><text class="terminal-r7" x="73.2" y="922.8" textLength="146.4" clip-path="url(#terminal-line-37)">/unleanstall</text><text class="terminal-r1" x="219.6" y="922.8" textLength="341.6" clip-path="url(#terminal-line-37)">:&#160;Uninstall&#160;the&#160;Lean&#160;4&#160;agent</text><text class="terminal-r1" x="1464" y="922.8" textLength="12.2" clip-path="url(#terminal-line-37)">
</text><text class="terminal-r4" x="24.4" y="947.2" textLength="12.2" clip-path="url(#terminal-line-38)"></text><text class="terminal-r1" x="48.8" y="947.2" textLength="24.4" clip-path="url(#terminal-line-38)">&#160;</text><text class="terminal-r7" x="73.2" y="947.2" textLength="85.4" clip-path="url(#terminal-line-38)">/rewind</text><text class="terminal-r1" x="158.6" y="947.2" textLength="366" clip-path="url(#terminal-line-38)">:&#160;Rewind&#160;to&#160;a&#160;previous&#160;message</text><text class="terminal-r1" x="1464" y="947.2" textLength="12.2" clip-path="url(#terminal-line-38)">
</text><text class="terminal-r4" x="24.4" y="971.6" textLength="12.2" clip-path="url(#terminal-line-39)"></text><text class="terminal-r1" x="48.8" y="971.6" textLength="24.4" clip-path="url(#terminal-line-39)">&#160;</text><text class="terminal-r7" x="73.2" y="971.6" textLength="183" clip-path="url(#terminal-line-39)">/data-retention</text><text class="terminal-r1" x="256.2" y="971.6" textLength="402.6" clip-path="url(#terminal-line-39)">:&#160;Show&#160;data&#160;retention&#160;information</text><text class="terminal-r1" x="1464" y="971.6" textLength="12.2" clip-path="url(#terminal-line-39)">
<text class="terminal-r1" x="1464" y="20" textLength="12.2" clip-path="url(#terminal-line-0)">
</text><text class="terminal-r2" x="24.4" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)"></text><text class="terminal-r3" x="48.8" y="44.4" textLength="219.6" clip-path="url(#terminal-line-1)">Keyboard&#160;Shortcuts</text><text class="terminal-r1" x="1464" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">
</text><text class="terminal-r2" x="24.4" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)"></text><text class="terminal-r1" x="48.8" y="68.8" textLength="24.4" clip-path="url(#terminal-line-2)">&#160;</text><text class="terminal-r4" x="73.2" y="68.8" textLength="61" clip-path="url(#terminal-line-2)">Enter</text><text class="terminal-r1" x="134.2" y="68.8" textLength="183" clip-path="url(#terminal-line-2)">&#160;Submit&#160;message</text><text class="terminal-r5" x="1451.8" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)"></text><text class="terminal-r1" x="1464" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">
</text><text class="terminal-r2" x="24.4" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)"></text><text class="terminal-r1" x="48.8" y="93.2" textLength="24.4" clip-path="url(#terminal-line-3)">&#160;</text><text class="terminal-r4" x="73.2" y="93.2" textLength="73.2" clip-path="url(#terminal-line-3)">Ctrl+J</text><text class="terminal-r1" x="146.4" y="93.2" textLength="36.6" clip-path="url(#terminal-line-3)">&#160;/&#160;</text><text class="terminal-r4" x="183" y="93.2" textLength="134.2" clip-path="url(#terminal-line-3)">Shift+Enter</text><text class="terminal-r1" x="317.2" y="93.2" textLength="183" clip-path="url(#terminal-line-3)">&#160;Insert&#160;newline</text><text class="terminal-r1" x="1464" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">
</text><text class="terminal-r2" x="24.4" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)"></text><text class="terminal-r1" x="48.8" y="117.6" textLength="24.4" clip-path="url(#terminal-line-4)">&#160;</text><text class="terminal-r4" x="73.2" y="117.6" textLength="73.2" clip-path="url(#terminal-line-4)">Escape</text><text class="terminal-r1" x="146.4" y="117.6" textLength="402.6" clip-path="url(#terminal-line-4)">&#160;Interrupt&#160;agent&#160;or&#160;close&#160;dialogs</text><text class="terminal-r1" x="1464" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">
</text><text class="terminal-r2" x="24.4" y="142" textLength="12.2" clip-path="url(#terminal-line-5)"></text><text class="terminal-r1" x="48.8" y="142" textLength="24.4" clip-path="url(#terminal-line-5)">&#160;</text><text class="terminal-r4" x="73.2" y="142" textLength="73.2" clip-path="url(#terminal-line-5)">Ctrl+C</text><text class="terminal-r1" x="146.4" y="142" textLength="463.6" clip-path="url(#terminal-line-5)">&#160;Quit&#160;(or&#160;clear&#160;input&#160;if&#160;text&#160;present)</text><text class="terminal-r1" x="1464" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">
</text><text class="terminal-r2" x="24.4" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)"></text><text class="terminal-r1" x="48.8" y="166.4" textLength="24.4" clip-path="url(#terminal-line-6)">&#160;</text><text class="terminal-r4" x="73.2" y="166.4" textLength="73.2" clip-path="url(#terminal-line-6)">Ctrl+G</text><text class="terminal-r1" x="146.4" y="166.4" textLength="366" clip-path="url(#terminal-line-6)">&#160;Edit&#160;input&#160;in&#160;external&#160;editor</text><text class="terminal-r1" x="1464" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">
</text><text class="terminal-r2" x="24.4" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)"></text><text class="terminal-r1" x="48.8" y="190.8" textLength="24.4" clip-path="url(#terminal-line-7)">&#160;</text><text class="terminal-r4" x="73.2" y="190.8" textLength="73.2" clip-path="url(#terminal-line-7)">Ctrl+O</text><text class="terminal-r1" x="146.4" y="190.8" textLength="292.8" clip-path="url(#terminal-line-7)">&#160;Toggle&#160;tool&#160;output&#160;view</text><text class="terminal-r1" x="1464" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">
</text><text class="terminal-r2" x="24.4" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)"></text><text class="terminal-r1" x="48.8" y="215.2" textLength="24.4" clip-path="url(#terminal-line-8)">&#160;</text><text class="terminal-r4" x="73.2" y="215.2" textLength="109.8" clip-path="url(#terminal-line-8)">Shift+Tab</text><text class="terminal-r1" x="183" y="215.2" textLength="512.4" clip-path="url(#terminal-line-8)">&#160;Cycle&#160;through&#160;agents&#160;(default,&#160;plan,&#160;...)</text><text class="terminal-r1" x="1464" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">
</text><text class="terminal-r2" x="24.4" y="239.6" textLength="12.2" clip-path="url(#terminal-line-9)"></text><text class="terminal-r1" x="48.8" y="239.6" textLength="24.4" clip-path="url(#terminal-line-9)">&#160;</text><text class="terminal-r4" x="73.2" y="239.6" textLength="73.2" clip-path="url(#terminal-line-9)">Alt+↑↓</text><text class="terminal-r1" x="146.4" y="239.6" textLength="36.6" clip-path="url(#terminal-line-9)">&#160;/&#160;</text><text class="terminal-r4" x="183" y="239.6" textLength="97.6" clip-path="url(#terminal-line-9)">Ctrl+P/N</text><text class="terminal-r1" x="280.6" y="239.6" textLength="390.4" clip-path="url(#terminal-line-9)">&#160;Rewind&#160;to&#160;previous/next&#160;message</text><text class="terminal-r1" x="1464" y="239.6" textLength="12.2" clip-path="url(#terminal-line-9)">
</text><text class="terminal-r2" x="24.4" y="264" textLength="12.2" clip-path="url(#terminal-line-10)"></text><text class="terminal-r1" x="1464" y="264" textLength="12.2" clip-path="url(#terminal-line-10)">
</text><text class="terminal-r2" x="24.4" y="288.4" textLength="12.2" clip-path="url(#terminal-line-11)"></text><text class="terminal-r3" x="48.8" y="288.4" textLength="195.2" clip-path="url(#terminal-line-11)">Special&#160;Features</text><text class="terminal-r1" x="1464" y="288.4" textLength="12.2" clip-path="url(#terminal-line-11)">
</text><text class="terminal-r2" x="24.4" y="312.8" textLength="12.2" clip-path="url(#terminal-line-12)"></text><text class="terminal-r1" x="1464" y="312.8" textLength="12.2" clip-path="url(#terminal-line-12)">
</text><text class="terminal-r2" x="24.4" y="337.2" textLength="12.2" clip-path="url(#terminal-line-13)"></text><text class="terminal-r1" x="48.8" y="337.2" textLength="24.4" clip-path="url(#terminal-line-13)">&#160;</text><text class="terminal-r4" x="73.2" y="337.2" textLength="122" clip-path="url(#terminal-line-13)">!&lt;command&gt;</text><text class="terminal-r1" x="195.2" y="337.2" textLength="366" clip-path="url(#terminal-line-13)">&#160;Execute&#160;bash&#160;command&#160;directly</text><text class="terminal-r1" x="1464" y="337.2" textLength="12.2" clip-path="url(#terminal-line-13)">
</text><text class="terminal-r2" x="24.4" y="361.6" textLength="12.2" clip-path="url(#terminal-line-14)"></text><text class="terminal-r1" x="48.8" y="361.6" textLength="24.4" clip-path="url(#terminal-line-14)">&#160;</text><text class="terminal-r4" x="73.2" y="361.6" textLength="170.8" clip-path="url(#terminal-line-14)">@path/to/file/</text><text class="terminal-r1" x="244" y="361.6" textLength="305" clip-path="url(#terminal-line-14)">&#160;Autocompletes&#160;file&#160;paths</text><text class="terminal-r1" x="1464" y="361.6" textLength="12.2" clip-path="url(#terminal-line-14)">
</text><text class="terminal-r2" x="24.4" y="386" textLength="12.2" clip-path="url(#terminal-line-15)"></text><text class="terminal-r1" x="1464" y="386" textLength="12.2" clip-path="url(#terminal-line-15)">
</text><text class="terminal-r2" x="24.4" y="410.4" textLength="12.2" clip-path="url(#terminal-line-16)"></text><text class="terminal-r3" x="48.8" y="410.4" textLength="97.6" clip-path="url(#terminal-line-16)">Commands</text><text class="terminal-r1" x="1464" y="410.4" textLength="12.2" clip-path="url(#terminal-line-16)">
</text><text class="terminal-r2" x="24.4" y="434.8" textLength="12.2" clip-path="url(#terminal-line-17)"></text><text class="terminal-r1" x="1464" y="434.8" textLength="12.2" clip-path="url(#terminal-line-17)">
</text><text class="terminal-r2" x="24.4" y="459.2" textLength="12.2" clip-path="url(#terminal-line-18)"></text><text class="terminal-r1" x="48.8" y="459.2" textLength="24.4" clip-path="url(#terminal-line-18)">&#160;</text><text class="terminal-r4" x="73.2" y="459.2" textLength="61" clip-path="url(#terminal-line-18)">/help</text><text class="terminal-r1" x="134.2" y="459.2" textLength="231.8" clip-path="url(#terminal-line-18)">:&#160;Show&#160;help&#160;message</text><text class="terminal-r1" x="1464" y="459.2" textLength="12.2" clip-path="url(#terminal-line-18)">
</text><text class="terminal-r2" x="24.4" y="483.6" textLength="12.2" clip-path="url(#terminal-line-19)"></text><text class="terminal-r1" x="48.8" y="483.6" textLength="24.4" clip-path="url(#terminal-line-19)">&#160;</text><text class="terminal-r4" x="73.2" y="483.6" textLength="85.4" clip-path="url(#terminal-line-19)">/config</text><text class="terminal-r1" x="158.6" y="483.6" textLength="268.4" clip-path="url(#terminal-line-19)">:&#160;Edit&#160;config&#160;settings</text><text class="terminal-r1" x="1464" y="483.6" textLength="12.2" clip-path="url(#terminal-line-19)">
</text><text class="terminal-r2" x="24.4" y="508" textLength="12.2" clip-path="url(#terminal-line-20)"></text><text class="terminal-r1" x="48.8" y="508" textLength="24.4" clip-path="url(#terminal-line-20)">&#160;</text><text class="terminal-r4" x="73.2" y="508" textLength="73.2" clip-path="url(#terminal-line-20)">/model</text><text class="terminal-r1" x="146.4" y="508" textLength="256.2" clip-path="url(#terminal-line-20)">:&#160;Select&#160;active&#160;model</text><text class="terminal-r1" x="1464" y="508" textLength="12.2" clip-path="url(#terminal-line-20)">
</text><text class="terminal-r2" x="24.4" y="532.4" textLength="12.2" clip-path="url(#terminal-line-21)"></text><text class="terminal-r1" x="48.8" y="532.4" textLength="24.4" clip-path="url(#terminal-line-21)">&#160;</text><text class="terminal-r4" x="73.2" y="532.4" textLength="109.8" clip-path="url(#terminal-line-21)">/thinking</text><text class="terminal-r1" x="183" y="532.4" textLength="280.6" clip-path="url(#terminal-line-21)">:&#160;Select&#160;thinking&#160;level</text><text class="terminal-r1" x="1464" y="532.4" textLength="12.2" clip-path="url(#terminal-line-21)">
</text><text class="terminal-r2" x="24.4" y="556.8" textLength="12.2" clip-path="url(#terminal-line-22)"></text><text class="terminal-r1" x="48.8" y="556.8" textLength="24.4" clip-path="url(#terminal-line-22)">&#160;</text><text class="terminal-r4" x="73.2" y="556.8" textLength="85.4" clip-path="url(#terminal-line-22)">/reload</text><text class="terminal-r1" x="158.6" y="556.8" textLength="780.8" clip-path="url(#terminal-line-22)">:&#160;Reload&#160;configuration,&#160;agent&#160;instructions,&#160;and&#160;skills&#160;from&#160;disk</text><text class="terminal-r1" x="1464" y="556.8" textLength="12.2" clip-path="url(#terminal-line-22)">
</text><text class="terminal-r2" x="24.4" y="581.2" textLength="12.2" clip-path="url(#terminal-line-23)"></text><text class="terminal-r1" x="48.8" y="581.2" textLength="24.4" clip-path="url(#terminal-line-23)">&#160;</text><text class="terminal-r4" x="73.2" y="581.2" textLength="73.2" clip-path="url(#terminal-line-23)">/clear</text><text class="terminal-r1" x="146.4" y="581.2" textLength="341.6" clip-path="url(#terminal-line-23)">:&#160;Clear&#160;conversation&#160;history</text><text class="terminal-r1" x="1464" y="581.2" textLength="12.2" clip-path="url(#terminal-line-23)">
</text><text class="terminal-r2" x="24.4" y="605.6" textLength="12.2" clip-path="url(#terminal-line-24)"></text><text class="terminal-r1" x="48.8" y="605.6" textLength="24.4" clip-path="url(#terminal-line-24)">&#160;</text><text class="terminal-r4" x="73.2" y="605.6" textLength="61" clip-path="url(#terminal-line-24)">/copy</text><text class="terminal-r1" x="134.2" y="605.6" textLength="561.2" clip-path="url(#terminal-line-24)">:&#160;Copy&#160;the&#160;last&#160;agent&#160;message&#160;to&#160;the&#160;clipboard</text><text class="terminal-r1" x="1464" y="605.6" textLength="12.2" clip-path="url(#terminal-line-24)">
</text><text class="terminal-r2" x="24.4" y="630" textLength="12.2" clip-path="url(#terminal-line-25)"></text><text class="terminal-r1" x="48.8" y="630" textLength="24.4" clip-path="url(#terminal-line-25)">&#160;</text><text class="terminal-r4" x="73.2" y="630" textLength="48.8" clip-path="url(#terminal-line-25)">/log</text><text class="terminal-r1" x="122" y="630" textLength="524.6" clip-path="url(#terminal-line-25)">:&#160;Show&#160;path&#160;to&#160;current&#160;interaction&#160;log&#160;file</text><text class="terminal-r1" x="1464" y="630" textLength="12.2" clip-path="url(#terminal-line-25)">
</text><text class="terminal-r2" x="24.4" y="654.4" textLength="12.2" clip-path="url(#terminal-line-26)"></text><text class="terminal-r1" x="48.8" y="654.4" textLength="24.4" clip-path="url(#terminal-line-26)">&#160;</text><text class="terminal-r4" x="73.2" y="654.4" textLength="73.2" clip-path="url(#terminal-line-26)">/debug</text><text class="terminal-r1" x="146.4" y="654.4" textLength="268.4" clip-path="url(#terminal-line-26)">:&#160;Toggle&#160;debug&#160;console</text><text class="terminal-r1" x="1464" y="654.4" textLength="12.2" clip-path="url(#terminal-line-26)">
</text><text class="terminal-r2" x="24.4" y="678.8" textLength="12.2" clip-path="url(#terminal-line-27)"></text><text class="terminal-r1" x="48.8" y="678.8" textLength="24.4" clip-path="url(#terminal-line-27)">&#160;</text><text class="terminal-r4" x="73.2" y="678.8" textLength="97.6" clip-path="url(#terminal-line-27)">/compact</text><text class="terminal-r1" x="170.8" y="678.8" textLength="1171.2" clip-path="url(#terminal-line-27)">:&#160;Compact&#160;conversation&#160;history&#160;by&#160;summarizing.&#160;Optionally&#160;pass&#160;instructions&#160;to&#160;guide&#160;the&#160;summary</text><text class="terminal-r1" x="1464" y="678.8" textLength="12.2" clip-path="url(#terminal-line-27)">
</text><text class="terminal-r2" x="24.4" y="703.2" textLength="12.2" clip-path="url(#terminal-line-28)"></text><text class="terminal-r1" x="48.8" y="703.2" textLength="24.4" clip-path="url(#terminal-line-28)">&#160;</text><text class="terminal-r4" x="73.2" y="703.2" textLength="61" clip-path="url(#terminal-line-28)">/exit</text><text class="terminal-r1" x="134.2" y="703.2" textLength="268.4" clip-path="url(#terminal-line-28)">:&#160;Exit&#160;the&#160;application</text><text class="terminal-r1" x="1464" y="703.2" textLength="12.2" clip-path="url(#terminal-line-28)">
</text><text class="terminal-r2" x="24.4" y="727.6" textLength="12.2" clip-path="url(#terminal-line-29)"></text><text class="terminal-r1" x="48.8" y="727.6" textLength="24.4" clip-path="url(#terminal-line-29)">&#160;</text><text class="terminal-r4" x="73.2" y="727.6" textLength="85.4" clip-path="url(#terminal-line-29)">/status</text><text class="terminal-r1" x="158.6" y="727.6" textLength="317.2" clip-path="url(#terminal-line-29)">:&#160;Display&#160;agent&#160;statistics</text><text class="terminal-r1" x="1464" y="727.6" textLength="12.2" clip-path="url(#terminal-line-29)">
</text><text class="terminal-r2" x="24.4" y="752" textLength="12.2" clip-path="url(#terminal-line-30)"></text><text class="terminal-r1" x="48.8" y="752" textLength="24.4" clip-path="url(#terminal-line-30)">&#160;</text><text class="terminal-r4" x="73.2" y="752" textLength="146.4" clip-path="url(#terminal-line-30)">/proxy-setup</text><text class="terminal-r1" x="219.6" y="752" textLength="561.2" clip-path="url(#terminal-line-30)">:&#160;Configure&#160;proxy&#160;and&#160;SSL&#160;certificate&#160;settings</text><text class="terminal-r1" x="1464" y="752" textLength="12.2" clip-path="url(#terminal-line-30)">
</text><text class="terminal-r2" x="24.4" y="776.4" textLength="12.2" clip-path="url(#terminal-line-31)"></text><text class="terminal-r1" x="48.8" y="776.4" textLength="24.4" clip-path="url(#terminal-line-31)">&#160;</text><text class="terminal-r4" x="73.2" y="776.4" textLength="109.8" clip-path="url(#terminal-line-31)">/continue</text><text class="terminal-r1" x="183" y="776.4" textLength="24.4" clip-path="url(#terminal-line-31)">,&#160;</text><text class="terminal-r4" x="207.4" y="776.4" textLength="85.4" clip-path="url(#terminal-line-31)">/resume</text><text class="terminal-r1" x="292.8" y="776.4" textLength="402.6" clip-path="url(#terminal-line-31)">:&#160;Browse&#160;and&#160;resume&#160;past&#160;sessions</text><text class="terminal-r1" x="1464" y="776.4" textLength="12.2" clip-path="url(#terminal-line-31)">
</text><text class="terminal-r2" x="24.4" y="800.8" textLength="12.2" clip-path="url(#terminal-line-32)"></text><text class="terminal-r1" x="48.8" y="800.8" textLength="24.4" clip-path="url(#terminal-line-32)">&#160;</text><text class="terminal-r4" x="73.2" y="800.8" textLength="85.4" clip-path="url(#terminal-line-32)">/rename</text><text class="terminal-r1" x="158.6" y="800.8" textLength="341.6" clip-path="url(#terminal-line-32)">:&#160;Rename&#160;the&#160;current&#160;session</text><text class="terminal-r1" x="1464" y="800.8" textLength="12.2" clip-path="url(#terminal-line-32)">
</text><text class="terminal-r2" x="24.4" y="825.2" textLength="12.2" clip-path="url(#terminal-line-33)"></text><text class="terminal-r1" x="48.8" y="825.2" textLength="24.4" clip-path="url(#terminal-line-33)">&#160;</text><text class="terminal-r4" x="73.2" y="825.2" textLength="134.2" clip-path="url(#terminal-line-33)">/connectors</text><text class="terminal-r1" x="207.4" y="825.2" textLength="24.4" clip-path="url(#terminal-line-33)">,&#160;</text><text class="terminal-r4" x="231.8" y="825.2" textLength="48.8" clip-path="url(#terminal-line-33)">/mcp</text><text class="terminal-r1" x="280.6" y="825.2" textLength="939.4" clip-path="url(#terminal-line-33)">:&#160;Display&#160;available&#160;MCP&#160;servers&#160;and&#160;connectors.&#160;Pass&#160;a&#160;name&#160;to&#160;list&#160;its&#160;tools</text><text class="terminal-r1" x="1464" y="825.2" textLength="12.2" clip-path="url(#terminal-line-33)">
</text><text class="terminal-r2" x="24.4" y="849.6" textLength="12.2" clip-path="url(#terminal-line-34)"></text><text class="terminal-r1" x="48.8" y="849.6" textLength="24.4" clip-path="url(#terminal-line-34)">&#160;</text><text class="terminal-r4" x="73.2" y="849.6" textLength="73.2" clip-path="url(#terminal-line-34)">/voice</text><text class="terminal-r1" x="146.4" y="849.6" textLength="317.2" clip-path="url(#terminal-line-34)">:&#160;Configure&#160;voice&#160;settings</text><text class="terminal-r1" x="1464" y="849.6" textLength="12.2" clip-path="url(#terminal-line-34)">
</text><text class="terminal-r2" x="24.4" y="874" textLength="12.2" clip-path="url(#terminal-line-35)"></text><text class="terminal-r1" x="48.8" y="874" textLength="24.4" clip-path="url(#terminal-line-35)">&#160;</text><text class="terminal-r4" x="73.2" y="874" textLength="122" clip-path="url(#terminal-line-35)">/leanstall</text><text class="terminal-r1" x="195.2" y="874" textLength="463.6" clip-path="url(#terminal-line-35)">:&#160;Install&#160;the&#160;Lean&#160;4&#160;agent&#160;(leanstral)</text><text class="terminal-r1" x="1464" y="874" textLength="12.2" clip-path="url(#terminal-line-35)">
</text><text class="terminal-r2" x="24.4" y="898.4" textLength="12.2" clip-path="url(#terminal-line-36)"></text><text class="terminal-r1" x="48.8" y="898.4" textLength="24.4" clip-path="url(#terminal-line-36)">&#160;</text><text class="terminal-r4" x="73.2" y="898.4" textLength="146.4" clip-path="url(#terminal-line-36)">/unleanstall</text><text class="terminal-r1" x="219.6" y="898.4" textLength="341.6" clip-path="url(#terminal-line-36)">:&#160;Uninstall&#160;the&#160;Lean&#160;4&#160;agent</text><text class="terminal-r1" x="1464" y="898.4" textLength="12.2" clip-path="url(#terminal-line-36)">
</text><text class="terminal-r2" x="24.4" y="922.8" textLength="12.2" clip-path="url(#terminal-line-37)"></text><text class="terminal-r1" x="48.8" y="922.8" textLength="24.4" clip-path="url(#terminal-line-37)">&#160;</text><text class="terminal-r4" x="73.2" y="922.8" textLength="85.4" clip-path="url(#terminal-line-37)">/rewind</text><text class="terminal-r1" x="158.6" y="922.8" textLength="366" clip-path="url(#terminal-line-37)">:&#160;Rewind&#160;to&#160;a&#160;previous&#160;message</text><text class="terminal-r1" x="1464" y="922.8" textLength="12.2" clip-path="url(#terminal-line-37)">
</text><text class="terminal-r2" x="24.4" y="947.2" textLength="12.2" clip-path="url(#terminal-line-38)"></text><text class="terminal-r1" x="48.8" y="947.2" textLength="24.4" clip-path="url(#terminal-line-38)">&#160;</text><text class="terminal-r4" x="73.2" y="947.2" textLength="61" clip-path="url(#terminal-line-38)">/loop</text><text class="terminal-r1" x="134.2" y="947.2" textLength="427" clip-path="url(#terminal-line-38)">:&#160;Schedule&#160;a&#160;recurring&#160;prompt.&#160;Use&#160;</text><text class="terminal-r4" x="561.2" y="947.2" textLength="305" clip-path="url(#terminal-line-38)">/loop&#160;&lt;interval&gt;&#160;&lt;prompt&gt;</text><text class="terminal-r1" x="866.2" y="947.2" textLength="24.4" clip-path="url(#terminal-line-38)">,&#160;</text><text class="terminal-r4" x="890.6" y="947.2" textLength="122" clip-path="url(#terminal-line-38)">/loop&#160;list</text><text class="terminal-r1" x="1012.6" y="947.2" textLength="61" clip-path="url(#terminal-line-38)">,&#160;or&#160;</text><text class="terminal-r4" x="1073.6" y="947.2" textLength="256.2" clip-path="url(#terminal-line-38)">/loop&#160;cancel&#160;&lt;id|all&gt;</text><text class="terminal-r1" x="1464" y="947.2" textLength="12.2" clip-path="url(#terminal-line-38)">
</text><text class="terminal-r2" x="24.4" y="971.6" textLength="12.2" clip-path="url(#terminal-line-39)"></text><text class="terminal-r1" x="48.8" y="971.6" textLength="24.4" clip-path="url(#terminal-line-39)">&#160;</text><text class="terminal-r4" x="73.2" y="971.6" textLength="183" clip-path="url(#terminal-line-39)">/data-retention</text><text class="terminal-r1" x="256.2" y="971.6" textLength="402.6" clip-path="url(#terminal-line-39)">:&#160;Show&#160;data&#160;retention&#160;information</text><text class="terminal-r1" x="1464" y="971.6" textLength="12.2" clip-path="url(#terminal-line-39)">
</text><text class="terminal-r1" x="1464" y="996" textLength="12.2" clip-path="url(#terminal-line-40)">
</text><text class="terminal-r1" x="1464" y="1020.4" textLength="12.2" clip-path="url(#terminal-line-41)">
</text><text class="terminal-r4" x="0" y="1044.8" textLength="1464" clip-path="url(#terminal-line-42)">┌────────────────────────────────────────────────────────────────────────────────────────────────────────────&#160;default&#160;─┐</text><text class="terminal-r1" x="1464" y="1044.8" textLength="12.2" clip-path="url(#terminal-line-42)">
</text><text class="terminal-r4" x="0" y="1069.2" textLength="12.2" clip-path="url(#terminal-line-43)"></text><text class="terminal-r8" x="24.4" y="1069.2" textLength="12.2" clip-path="url(#terminal-line-43)">&gt;</text><text class="terminal-r4" x="1451.8" y="1069.2" textLength="12.2" clip-path="url(#terminal-line-43)"></text><text class="terminal-r1" x="1464" y="1069.2" textLength="12.2" clip-path="url(#terminal-line-43)">
</text><text class="terminal-r4" x="0" y="1093.6" textLength="12.2" clip-path="url(#terminal-line-44)"></text><text class="terminal-r4" x="1451.8" y="1093.6" textLength="12.2" clip-path="url(#terminal-line-44)"></text><text class="terminal-r1" x="1464" y="1093.6" textLength="12.2" clip-path="url(#terminal-line-44)">
</text><text class="terminal-r4" x="0" y="1118" textLength="12.2" clip-path="url(#terminal-line-45)"></text><text class="terminal-r4" x="1451.8" y="1118" textLength="12.2" clip-path="url(#terminal-line-45)"></text><text class="terminal-r1" x="1464" y="1118" textLength="12.2" clip-path="url(#terminal-line-45)">
</text><text class="terminal-r4" x="0" y="1142.4" textLength="1464" clip-path="url(#terminal-line-46)">└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘</text><text class="terminal-r1" x="1464" y="1142.4" textLength="12.2" clip-path="url(#terminal-line-46)">
</text><text class="terminal-r4" x="0" y="1166.8" textLength="158.6" clip-path="url(#terminal-line-47)">/test/workdir</text><text class="terminal-r4" x="1256.6" y="1166.8" textLength="207.4" clip-path="url(#terminal-line-47)">0%&#160;of&#160;200k&#160;tokens</text>
</text><text class="terminal-r2" x="0" y="1044.8" textLength="1464" clip-path="url(#terminal-line-42)">┌────────────────────────────────────────────────────────────────────────────────────────────────────────────&#160;default&#160;─┐</text><text class="terminal-r1" x="1464" y="1044.8" textLength="12.2" clip-path="url(#terminal-line-42)">
</text><text class="terminal-r2" x="0" y="1069.2" textLength="12.2" clip-path="url(#terminal-line-43)"></text><text class="terminal-r7" x="24.4" y="1069.2" textLength="12.2" clip-path="url(#terminal-line-43)">&gt;</text><text class="terminal-r2" x="1451.8" y="1069.2" textLength="12.2" clip-path="url(#terminal-line-43)"></text><text class="terminal-r1" x="1464" y="1069.2" textLength="12.2" clip-path="url(#terminal-line-43)">
</text><text class="terminal-r2" x="0" y="1093.6" textLength="12.2" clip-path="url(#terminal-line-44)"></text><text class="terminal-r2" x="1451.8" y="1093.6" textLength="12.2" clip-path="url(#terminal-line-44)"></text><text class="terminal-r1" x="1464" y="1093.6" textLength="12.2" clip-path="url(#terminal-line-44)">
</text><text class="terminal-r2" x="0" y="1118" textLength="12.2" clip-path="url(#terminal-line-45)"></text><text class="terminal-r2" x="1451.8" y="1118" textLength="12.2" clip-path="url(#terminal-line-45)"></text><text class="terminal-r1" x="1464" y="1118" textLength="12.2" clip-path="url(#terminal-line-45)">
</text><text class="terminal-r2" x="0" y="1142.4" textLength="1464" clip-path="url(#terminal-line-46)">└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘</text><text class="terminal-r1" x="1464" y="1142.4" textLength="12.2" clip-path="url(#terminal-line-46)">
</text><text class="terminal-r2" x="0" y="1166.8" textLength="158.6" clip-path="url(#terminal-line-47)">/test/workdir</text><text class="terminal-r2" x="1256.6" y="1166.8" textLength="207.4" clip-path="url(#terminal-line-47)">0%&#160;of&#160;200k&#160;tokens</text>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Before After
Before After

View file

@ -196,47 +196,47 @@
</g>
<g transform="translate(9, 41)" clip-path="url(#terminal-clip-terminal)">
<rect fill="#4b4e55" x="1451.8" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="221.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="245.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="269.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="294.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="318.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="343.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="367.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="391.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="416.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="440.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="465.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="489.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="513.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="538.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="562.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="587.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="611.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="635.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="660.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="684.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="709.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="733.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="757.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="782.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="806.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="831.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="855.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="879.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="904.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="928.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="953.1" width="12.2" height="24.65" shape-rendering="crispEdges"/>
<rect fill="#4b4e55" x="1451.8" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="221.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="245.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="269.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="294.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="318.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="343.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="367.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="391.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="416.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="440.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="465.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="489.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="513.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="538.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="562.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="587.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="611.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="635.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="660.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="684.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="709.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="733.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="757.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="782.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="806.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="831.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="855.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="879.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="904.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="928.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#4b4e55" x="1451.8" y="953.1" width="12.2" height="24.65" shape-rendering="crispEdges"/>
<g class="terminal-matrix">
<text class="terminal-r1" x="1464" y="20" textLength="12.2" clip-path="url(#terminal-line-0)">
</text><text class="terminal-r2" x="24.4" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)"></text><text class="terminal-r3" x="48.8" y="44.4" textLength="219.6" clip-path="url(#terminal-line-1)">Keyboard&#160;Shortcuts</text><text class="terminal-r1" x="1464" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">
</text><text class="terminal-r2" x="24.4" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)"></text><text class="terminal-r1" x="48.8" y="68.8" textLength="24.4" clip-path="url(#terminal-line-2)">&#160;</text><text class="terminal-r4" x="73.2" y="68.8" textLength="61" clip-path="url(#terminal-line-2)">Enter</text><text class="terminal-r1" x="134.2" y="68.8" textLength="183" clip-path="url(#terminal-line-2)">&#160;Submit&#160;message</text><text class="terminal-r5" x="1451.8" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)"></text><text class="terminal-r1" x="1464" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">
</text><text class="terminal-r2" x="24.4" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)"></text><text class="terminal-r1" x="48.8" y="93.2" textLength="24.4" clip-path="url(#terminal-line-3)">&#160;</text><text class="terminal-r4" x="73.2" y="93.2" textLength="73.2" clip-path="url(#terminal-line-3)">Ctrl+J</text><text class="terminal-r1" x="146.4" y="93.2" textLength="36.6" clip-path="url(#terminal-line-3)">&#160;/&#160;</text><text class="terminal-r4" x="183" y="93.2" textLength="134.2" clip-path="url(#terminal-line-3)">Shift+Enter</text><text class="terminal-r1" x="317.2" y="93.2" textLength="183" clip-path="url(#terminal-line-3)">&#160;Insert&#160;newline</text><text class="terminal-r1" x="1464" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">
</text><text class="terminal-r2" x="24.4" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)"></text><text class="terminal-r1" x="48.8" y="117.6" textLength="24.4" clip-path="url(#terminal-line-4)">&#160;</text><text class="terminal-r4" x="73.2" y="117.6" textLength="73.2" clip-path="url(#terminal-line-4)">Escape</text><text class="terminal-r1" x="146.4" y="117.6" textLength="402.6" clip-path="url(#terminal-line-4)">&#160;Interrupt&#160;agent&#160;or&#160;close&#160;dialogs</text><text class="terminal-r1" x="1464" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">
</text><text class="terminal-r2" x="24.4" y="142" textLength="12.2" clip-path="url(#terminal-line-5)"></text><text class="terminal-r1" x="48.8" y="142" textLength="24.4" clip-path="url(#terminal-line-5)">&#160;</text><text class="terminal-r4" x="73.2" y="142" textLength="73.2" clip-path="url(#terminal-line-5)">Ctrl+C</text><text class="terminal-r1" x="146.4" y="142" textLength="463.6" clip-path="url(#terminal-line-5)">&#160;Quit&#160;(or&#160;clear&#160;input&#160;if&#160;text&#160;present)</text><text class="terminal-r1" x="1464" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">
</text><text class="terminal-r2" x="24.4" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)"></text><text class="terminal-r1" x="48.8" y="166.4" textLength="24.4" clip-path="url(#terminal-line-6)">&#160;</text><text class="terminal-r4" x="73.2" y="166.4" textLength="73.2" clip-path="url(#terminal-line-6)">Ctrl+G</text><text class="terminal-r1" x="146.4" y="166.4" textLength="366" clip-path="url(#terminal-line-6)">&#160;Edit&#160;input&#160;in&#160;external&#160;editor</text><text class="terminal-r1" x="1464" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">
</text><text class="terminal-r2" x="24.4" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)"></text><text class="terminal-r1" x="48.8" y="190.8" textLength="24.4" clip-path="url(#terminal-line-7)">&#160;</text><text class="terminal-r4" x="73.2" y="190.8" textLength="73.2" clip-path="url(#terminal-line-7)">Ctrl+O</text><text class="terminal-r1" x="146.4" y="190.8" textLength="292.8" clip-path="url(#terminal-line-7)">&#160;Toggle&#160;tool&#160;output&#160;view</text><text class="terminal-r1" x="1464" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">
</text><text class="terminal-r2" x="24.4" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)"></text><text class="terminal-r1" x="48.8" y="215.2" textLength="24.4" clip-path="url(#terminal-line-8)">&#160;</text><text class="terminal-r4" x="73.2" y="215.2" textLength="109.8" clip-path="url(#terminal-line-8)">Shift+Tab</text><text class="terminal-r1" x="183" y="215.2" textLength="512.4" clip-path="url(#terminal-line-8)">&#160;Cycle&#160;through&#160;agents&#160;(default,&#160;plan,&#160;...)</text><text class="terminal-r1" x="1464" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">
</text><text class="terminal-r2" x="24.4" y="239.6" textLength="12.2" clip-path="url(#terminal-line-9)"></text><text class="terminal-r1" x="48.8" y="239.6" textLength="24.4" clip-path="url(#terminal-line-9)">&#160;</text><text class="terminal-r4" x="73.2" y="239.6" textLength="73.2" clip-path="url(#terminal-line-9)">Alt+↑↓</text><text class="terminal-r1" x="146.4" y="239.6" textLength="36.6" clip-path="url(#terminal-line-9)">&#160;/&#160;</text><text class="terminal-r4" x="183" y="239.6" textLength="97.6" clip-path="url(#terminal-line-9)">Ctrl+P/N</text><text class="terminal-r1" x="280.6" y="239.6" textLength="390.4" clip-path="url(#terminal-line-9)">&#160;Rewind&#160;to&#160;previous/next&#160;message</text><text class="terminal-r1" x="1464" y="239.6" textLength="12.2" clip-path="url(#terminal-line-9)">
</text><text class="terminal-r2" x="24.4" y="264" textLength="12.2" clip-path="url(#terminal-line-10)"></text><text class="terminal-r1" x="1464" y="264" textLength="12.2" clip-path="url(#terminal-line-10)">
</text><text class="terminal-r2" x="24.4" y="288.4" textLength="12.2" clip-path="url(#terminal-line-11)"></text><text class="terminal-r3" x="48.8" y="288.4" textLength="195.2" clip-path="url(#terminal-line-11)">Special&#160;Features</text><text class="terminal-r1" x="1464" y="288.4" textLength="12.2" clip-path="url(#terminal-line-11)">
</text><text class="terminal-r2" x="24.4" y="312.8" textLength="12.2" clip-path="url(#terminal-line-12)"></text><text class="terminal-r1" x="1464" y="312.8" textLength="12.2" clip-path="url(#terminal-line-12)">
</text><text class="terminal-r2" x="24.4" y="337.2" textLength="12.2" clip-path="url(#terminal-line-13)"></text><text class="terminal-r1" x="48.8" y="337.2" textLength="24.4" clip-path="url(#terminal-line-13)">&#160;</text><text class="terminal-r4" x="73.2" y="337.2" textLength="122" clip-path="url(#terminal-line-13)">!&lt;command&gt;</text><text class="terminal-r1" x="195.2" y="337.2" textLength="366" clip-path="url(#terminal-line-13)">&#160;Execute&#160;bash&#160;command&#160;directly</text><text class="terminal-r1" x="1464" y="337.2" textLength="12.2" clip-path="url(#terminal-line-13)">
</text><text class="terminal-r2" x="24.4" y="361.6" textLength="12.2" clip-path="url(#terminal-line-14)"></text><text class="terminal-r1" x="48.8" y="361.6" textLength="24.4" clip-path="url(#terminal-line-14)">&#160;</text><text class="terminal-r4" x="73.2" y="361.6" textLength="170.8" clip-path="url(#terminal-line-14)">@path/to/file/</text><text class="terminal-r1" x="244" y="361.6" textLength="305" clip-path="url(#terminal-line-14)">&#160;Autocompletes&#160;file&#160;paths</text><text class="terminal-r1" x="1464" y="361.6" textLength="12.2" clip-path="url(#terminal-line-14)">
</text><text class="terminal-r2" x="24.4" y="386" textLength="12.2" clip-path="url(#terminal-line-15)"></text><text class="terminal-r1" x="1464" y="386" textLength="12.2" clip-path="url(#terminal-line-15)">
</text><text class="terminal-r2" x="24.4" y="410.4" textLength="12.2" clip-path="url(#terminal-line-16)"></text><text class="terminal-r3" x="48.8" y="410.4" textLength="97.6" clip-path="url(#terminal-line-16)">Commands</text><text class="terminal-r1" x="1464" y="410.4" textLength="12.2" clip-path="url(#terminal-line-16)">
</text><text class="terminal-r2" x="24.4" y="434.8" textLength="12.2" clip-path="url(#terminal-line-17)"></text><text class="terminal-r1" x="1464" y="434.8" textLength="12.2" clip-path="url(#terminal-line-17)">
</text><text class="terminal-r2" x="24.4" y="459.2" textLength="12.2" clip-path="url(#terminal-line-18)"></text><text class="terminal-r1" x="48.8" y="459.2" textLength="24.4" clip-path="url(#terminal-line-18)">&#160;</text><text class="terminal-r4" x="73.2" y="459.2" textLength="61" clip-path="url(#terminal-line-18)">/help</text><text class="terminal-r1" x="134.2" y="459.2" textLength="231.8" clip-path="url(#terminal-line-18)">:&#160;Show&#160;help&#160;message</text><text class="terminal-r1" x="1464" y="459.2" textLength="12.2" clip-path="url(#terminal-line-18)">
</text><text class="terminal-r2" x="24.4" y="483.6" textLength="12.2" clip-path="url(#terminal-line-19)"></text><text class="terminal-r1" x="48.8" y="483.6" textLength="24.4" clip-path="url(#terminal-line-19)">&#160;</text><text class="terminal-r4" x="73.2" y="483.6" textLength="85.4" clip-path="url(#terminal-line-19)">/config</text><text class="terminal-r1" x="158.6" y="483.6" textLength="268.4" clip-path="url(#terminal-line-19)">:&#160;Edit&#160;config&#160;settings</text><text class="terminal-r1" x="1464" y="483.6" textLength="12.2" clip-path="url(#terminal-line-19)">
</text><text class="terminal-r2" x="24.4" y="508" textLength="12.2" clip-path="url(#terminal-line-20)"></text><text class="terminal-r1" x="48.8" y="508" textLength="24.4" clip-path="url(#terminal-line-20)">&#160;</text><text class="terminal-r4" x="73.2" y="508" textLength="73.2" clip-path="url(#terminal-line-20)">/model</text><text class="terminal-r1" x="146.4" y="508" textLength="256.2" clip-path="url(#terminal-line-20)">:&#160;Select&#160;active&#160;model</text><text class="terminal-r1" x="1464" y="508" textLength="12.2" clip-path="url(#terminal-line-20)">
</text><text class="terminal-r2" x="24.4" y="532.4" textLength="12.2" clip-path="url(#terminal-line-21)"></text><text class="terminal-r1" x="48.8" y="532.4" textLength="24.4" clip-path="url(#terminal-line-21)">&#160;</text><text class="terminal-r4" x="73.2" y="532.4" textLength="109.8" clip-path="url(#terminal-line-21)">/thinking</text><text class="terminal-r1" x="183" y="532.4" textLength="280.6" clip-path="url(#terminal-line-21)">:&#160;Select&#160;thinking&#160;level</text><text class="terminal-r1" x="1464" y="532.4" textLength="12.2" clip-path="url(#terminal-line-21)">
</text><text class="terminal-r2" x="24.4" y="556.8" textLength="12.2" clip-path="url(#terminal-line-22)"></text><text class="terminal-r1" x="48.8" y="556.8" textLength="24.4" clip-path="url(#terminal-line-22)">&#160;</text><text class="terminal-r4" x="73.2" y="556.8" textLength="85.4" clip-path="url(#terminal-line-22)">/reload</text><text class="terminal-r1" x="158.6" y="556.8" textLength="780.8" clip-path="url(#terminal-line-22)">:&#160;Reload&#160;configuration,&#160;agent&#160;instructions,&#160;and&#160;skills&#160;from&#160;disk</text><text class="terminal-r1" x="1464" y="556.8" textLength="12.2" clip-path="url(#terminal-line-22)">
</text><text class="terminal-r2" x="24.4" y="581.2" textLength="12.2" clip-path="url(#terminal-line-23)"></text><text class="terminal-r1" x="48.8" y="581.2" textLength="24.4" clip-path="url(#terminal-line-23)">&#160;</text><text class="terminal-r4" x="73.2" y="581.2" textLength="73.2" clip-path="url(#terminal-line-23)">/clear</text><text class="terminal-r1" x="146.4" y="581.2" textLength="341.6" clip-path="url(#terminal-line-23)">:&#160;Clear&#160;conversation&#160;history</text><text class="terminal-r1" x="1464" y="581.2" textLength="12.2" clip-path="url(#terminal-line-23)">
</text><text class="terminal-r2" x="24.4" y="605.6" textLength="12.2" clip-path="url(#terminal-line-24)"></text><text class="terminal-r1" x="48.8" y="605.6" textLength="24.4" clip-path="url(#terminal-line-24)">&#160;</text><text class="terminal-r4" x="73.2" y="605.6" textLength="61" clip-path="url(#terminal-line-24)">/copy</text><text class="terminal-r1" x="134.2" y="605.6" textLength="561.2" clip-path="url(#terminal-line-24)">:&#160;Copy&#160;the&#160;last&#160;agent&#160;message&#160;to&#160;the&#160;clipboard</text><text class="terminal-r1" x="1464" y="605.6" textLength="12.2" clip-path="url(#terminal-line-24)">
</text><text class="terminal-r2" x="24.4" y="630" textLength="12.2" clip-path="url(#terminal-line-25)"></text><text class="terminal-r1" x="48.8" y="630" textLength="24.4" clip-path="url(#terminal-line-25)">&#160;</text><text class="terminal-r4" x="73.2" y="630" textLength="48.8" clip-path="url(#terminal-line-25)">/log</text><text class="terminal-r1" x="122" y="630" textLength="524.6" clip-path="url(#terminal-line-25)">:&#160;Show&#160;path&#160;to&#160;current&#160;interaction&#160;log&#160;file</text><text class="terminal-r1" x="1464" y="630" textLength="12.2" clip-path="url(#terminal-line-25)">
</text><text class="terminal-r2" x="24.4" y="654.4" textLength="12.2" clip-path="url(#terminal-line-26)"></text><text class="terminal-r1" x="48.8" y="654.4" textLength="24.4" clip-path="url(#terminal-line-26)">&#160;</text><text class="terminal-r4" x="73.2" y="654.4" textLength="73.2" clip-path="url(#terminal-line-26)">/debug</text><text class="terminal-r1" x="146.4" y="654.4" textLength="268.4" clip-path="url(#terminal-line-26)">:&#160;Toggle&#160;debug&#160;console</text><text class="terminal-r1" x="1464" y="654.4" textLength="12.2" clip-path="url(#terminal-line-26)">
</text><text class="terminal-r2" x="24.4" y="678.8" textLength="12.2" clip-path="url(#terminal-line-27)"></text><text class="terminal-r1" x="48.8" y="678.8" textLength="24.4" clip-path="url(#terminal-line-27)">&#160;</text><text class="terminal-r4" x="73.2" y="678.8" textLength="97.6" clip-path="url(#terminal-line-27)">/compact</text><text class="terminal-r1" x="170.8" y="678.8" textLength="1171.2" clip-path="url(#terminal-line-27)">:&#160;Compact&#160;conversation&#160;history&#160;by&#160;summarizing.&#160;Optionally&#160;pass&#160;instructions&#160;to&#160;guide&#160;the&#160;summary</text><text class="terminal-r1" x="1464" y="678.8" textLength="12.2" clip-path="url(#terminal-line-27)">
</text><text class="terminal-r2" x="24.4" y="703.2" textLength="12.2" clip-path="url(#terminal-line-28)"></text><text class="terminal-r1" x="48.8" y="703.2" textLength="24.4" clip-path="url(#terminal-line-28)">&#160;</text><text class="terminal-r4" x="73.2" y="703.2" textLength="61" clip-path="url(#terminal-line-28)">/exit</text><text class="terminal-r1" x="134.2" y="703.2" textLength="268.4" clip-path="url(#terminal-line-28)">:&#160;Exit&#160;the&#160;application</text><text class="terminal-r1" x="1464" y="703.2" textLength="12.2" clip-path="url(#terminal-line-28)">
</text><text class="terminal-r2" x="24.4" y="727.6" textLength="12.2" clip-path="url(#terminal-line-29)"></text><text class="terminal-r1" x="48.8" y="727.6" textLength="24.4" clip-path="url(#terminal-line-29)">&#160;</text><text class="terminal-r4" x="73.2" y="727.6" textLength="85.4" clip-path="url(#terminal-line-29)">/status</text><text class="terminal-r1" x="158.6" y="727.6" textLength="317.2" clip-path="url(#terminal-line-29)">:&#160;Display&#160;agent&#160;statistics</text><text class="terminal-r1" x="1464" y="727.6" textLength="12.2" clip-path="url(#terminal-line-29)">
</text><text class="terminal-r2" x="24.4" y="752" textLength="12.2" clip-path="url(#terminal-line-30)"></text><text class="terminal-r1" x="48.8" y="752" textLength="24.4" clip-path="url(#terminal-line-30)">&#160;</text><text class="terminal-r4" x="73.2" y="752" textLength="109.8" clip-path="url(#terminal-line-30)">/teleport</text><text class="terminal-r1" x="183" y="752" textLength="378.2" clip-path="url(#terminal-line-30)">:&#160;Teleport&#160;session&#160;to&#160;Vibe&#160;Code</text><text class="terminal-r1" x="1464" y="752" textLength="12.2" clip-path="url(#terminal-line-30)">
</text><text class="terminal-r2" x="24.4" y="776.4" textLength="12.2" clip-path="url(#terminal-line-31)"></text><text class="terminal-r1" x="48.8" y="776.4" textLength="24.4" clip-path="url(#terminal-line-31)">&#160;</text><text class="terminal-r4" x="73.2" y="776.4" textLength="146.4" clip-path="url(#terminal-line-31)">/proxy-setup</text><text class="terminal-r1" x="219.6" y="776.4" textLength="561.2" clip-path="url(#terminal-line-31)">:&#160;Configure&#160;proxy&#160;and&#160;SSL&#160;certificate&#160;settings</text><text class="terminal-r1" x="1464" y="776.4" textLength="12.2" clip-path="url(#terminal-line-31)">
</text><text class="terminal-r2" x="24.4" y="800.8" textLength="12.2" clip-path="url(#terminal-line-32)"></text><text class="terminal-r1" x="48.8" y="800.8" textLength="24.4" clip-path="url(#terminal-line-32)">&#160;</text><text class="terminal-r4" x="73.2" y="800.8" textLength="109.8" clip-path="url(#terminal-line-32)">/continue</text><text class="terminal-r1" x="183" y="800.8" textLength="24.4" clip-path="url(#terminal-line-32)">,&#160;</text><text class="terminal-r4" x="207.4" y="800.8" textLength="85.4" clip-path="url(#terminal-line-32)">/resume</text><text class="terminal-r1" x="292.8" y="800.8" textLength="402.6" clip-path="url(#terminal-line-32)">:&#160;Browse&#160;and&#160;resume&#160;past&#160;sessions</text><text class="terminal-r1" x="1464" y="800.8" textLength="12.2" clip-path="url(#terminal-line-32)">
</text><text class="terminal-r2" x="24.4" y="825.2" textLength="12.2" clip-path="url(#terminal-line-33)"></text><text class="terminal-r1" x="48.8" y="825.2" textLength="24.4" clip-path="url(#terminal-line-33)">&#160;</text><text class="terminal-r4" x="73.2" y="825.2" textLength="85.4" clip-path="url(#terminal-line-33)">/rename</text><text class="terminal-r1" x="158.6" y="825.2" textLength="341.6" clip-path="url(#terminal-line-33)">:&#160;Rename&#160;the&#160;current&#160;session</text><text class="terminal-r1" x="1464" y="825.2" textLength="12.2" clip-path="url(#terminal-line-33)">
</text><text class="terminal-r2" x="24.4" y="849.6" textLength="12.2" clip-path="url(#terminal-line-34)"></text><text class="terminal-r1" x="48.8" y="849.6" textLength="24.4" clip-path="url(#terminal-line-34)">&#160;</text><text class="terminal-r4" x="73.2" y="849.6" textLength="134.2" clip-path="url(#terminal-line-34)">/connectors</text><text class="terminal-r1" x="207.4" y="849.6" textLength="24.4" clip-path="url(#terminal-line-34)">,&#160;</text><text class="terminal-r4" x="231.8" y="849.6" textLength="48.8" clip-path="url(#terminal-line-34)">/mcp</text><text class="terminal-r1" x="280.6" y="849.6" textLength="939.4" clip-path="url(#terminal-line-34)">:&#160;Display&#160;available&#160;MCP&#160;servers&#160;and&#160;connectors.&#160;Pass&#160;a&#160;name&#160;to&#160;list&#160;its&#160;tools</text><text class="terminal-r1" x="1464" y="849.6" textLength="12.2" clip-path="url(#terminal-line-34)">
</text><text class="terminal-r2" x="24.4" y="874" textLength="12.2" clip-path="url(#terminal-line-35)"></text><text class="terminal-r1" x="48.8" y="874" textLength="24.4" clip-path="url(#terminal-line-35)">&#160;</text><text class="terminal-r4" x="73.2" y="874" textLength="73.2" clip-path="url(#terminal-line-35)">/voice</text><text class="terminal-r1" x="146.4" y="874" textLength="317.2" clip-path="url(#terminal-line-35)">:&#160;Configure&#160;voice&#160;settings</text><text class="terminal-r1" x="1464" y="874" textLength="12.2" clip-path="url(#terminal-line-35)">
</text><text class="terminal-r2" x="24.4" y="898.4" textLength="12.2" clip-path="url(#terminal-line-36)"></text><text class="terminal-r1" x="48.8" y="898.4" textLength="24.4" clip-path="url(#terminal-line-36)">&#160;</text><text class="terminal-r4" x="73.2" y="898.4" textLength="122" clip-path="url(#terminal-line-36)">/leanstall</text><text class="terminal-r1" x="195.2" y="898.4" textLength="463.6" clip-path="url(#terminal-line-36)">:&#160;Install&#160;the&#160;Lean&#160;4&#160;agent&#160;(leanstral)</text><text class="terminal-r1" x="1464" y="898.4" textLength="12.2" clip-path="url(#terminal-line-36)">
</text><text class="terminal-r2" x="24.4" y="922.8" textLength="12.2" clip-path="url(#terminal-line-37)"></text><text class="terminal-r1" x="48.8" y="922.8" textLength="24.4" clip-path="url(#terminal-line-37)">&#160;</text><text class="terminal-r4" x="73.2" y="922.8" textLength="146.4" clip-path="url(#terminal-line-37)">/unleanstall</text><text class="terminal-r1" x="219.6" y="922.8" textLength="341.6" clip-path="url(#terminal-line-37)">:&#160;Uninstall&#160;the&#160;Lean&#160;4&#160;agent</text><text class="terminal-r1" x="1464" y="922.8" textLength="12.2" clip-path="url(#terminal-line-37)">
</text><text class="terminal-r2" x="24.4" y="947.2" textLength="12.2" clip-path="url(#terminal-line-38)"></text><text class="terminal-r1" x="48.8" y="947.2" textLength="24.4" clip-path="url(#terminal-line-38)">&#160;</text><text class="terminal-r4" x="73.2" y="947.2" textLength="85.4" clip-path="url(#terminal-line-38)">/rewind</text><text class="terminal-r1" x="158.6" y="947.2" textLength="366" clip-path="url(#terminal-line-38)">:&#160;Rewind&#160;to&#160;a&#160;previous&#160;message</text><text class="terminal-r1" x="1464" y="947.2" textLength="12.2" clip-path="url(#terminal-line-38)">
<text class="terminal-r2" x="24.4" y="20" textLength="12.2" clip-path="url(#terminal-line-0)"></text><text class="terminal-r3" x="48.8" y="20" textLength="219.6" clip-path="url(#terminal-line-0)">Keyboard&#160;Shortcuts</text><text class="terminal-r1" x="1464" y="20" textLength="12.2" clip-path="url(#terminal-line-0)">
</text><text class="terminal-r2" x="24.4" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)"></text><text class="terminal-r1" x="48.8" y="44.4" textLength="24.4" clip-path="url(#terminal-line-1)">&#160;</text><text class="terminal-r4" x="73.2" y="44.4" textLength="61" clip-path="url(#terminal-line-1)">Enter</text><text class="terminal-r1" x="134.2" y="44.4" textLength="183" clip-path="url(#terminal-line-1)">&#160;Submit&#160;message</text><text class="terminal-r1" x="1464" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">
</text><text class="terminal-r2" x="24.4" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)"></text><text class="terminal-r1" x="48.8" y="68.8" textLength="24.4" clip-path="url(#terminal-line-2)">&#160;</text><text class="terminal-r4" x="73.2" y="68.8" textLength="73.2" clip-path="url(#terminal-line-2)">Ctrl+J</text><text class="terminal-r1" x="146.4" y="68.8" textLength="36.6" clip-path="url(#terminal-line-2)">&#160;/&#160;</text><text class="terminal-r4" x="183" y="68.8" textLength="134.2" clip-path="url(#terminal-line-2)">Shift+Enter</text><text class="terminal-r1" x="317.2" y="68.8" textLength="183" clip-path="url(#terminal-line-2)">&#160;Insert&#160;newline</text><text class="terminal-r1" x="1464" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">
</text><text class="terminal-r2" x="24.4" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)"></text><text class="terminal-r1" x="48.8" y="93.2" textLength="24.4" clip-path="url(#terminal-line-3)">&#160;</text><text class="terminal-r4" x="73.2" y="93.2" textLength="73.2" clip-path="url(#terminal-line-3)">Escape</text><text class="terminal-r1" x="146.4" y="93.2" textLength="402.6" clip-path="url(#terminal-line-3)">&#160;Interrupt&#160;agent&#160;or&#160;close&#160;dialogs</text><text class="terminal-r5" x="1451.8" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)"></text><text class="terminal-r1" x="1464" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">
</text><text class="terminal-r2" x="24.4" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)"></text><text class="terminal-r1" x="48.8" y="117.6" textLength="24.4" clip-path="url(#terminal-line-4)">&#160;</text><text class="terminal-r4" x="73.2" y="117.6" textLength="73.2" clip-path="url(#terminal-line-4)">Ctrl+C</text><text class="terminal-r1" x="146.4" y="117.6" textLength="463.6" clip-path="url(#terminal-line-4)">&#160;Quit&#160;(or&#160;clear&#160;input&#160;if&#160;text&#160;present)</text><text class="terminal-r1" x="1464" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">
</text><text class="terminal-r2" x="24.4" y="142" textLength="12.2" clip-path="url(#terminal-line-5)"></text><text class="terminal-r1" x="48.8" y="142" textLength="24.4" clip-path="url(#terminal-line-5)">&#160;</text><text class="terminal-r4" x="73.2" y="142" textLength="73.2" clip-path="url(#terminal-line-5)">Ctrl+G</text><text class="terminal-r1" x="146.4" y="142" textLength="366" clip-path="url(#terminal-line-5)">&#160;Edit&#160;input&#160;in&#160;external&#160;editor</text><text class="terminal-r1" x="1464" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">
</text><text class="terminal-r2" x="24.4" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)"></text><text class="terminal-r1" x="48.8" y="166.4" textLength="24.4" clip-path="url(#terminal-line-6)">&#160;</text><text class="terminal-r4" x="73.2" y="166.4" textLength="73.2" clip-path="url(#terminal-line-6)">Ctrl+O</text><text class="terminal-r1" x="146.4" y="166.4" textLength="292.8" clip-path="url(#terminal-line-6)">&#160;Toggle&#160;tool&#160;output&#160;view</text><text class="terminal-r1" x="1464" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">
</text><text class="terminal-r2" x="24.4" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)"></text><text class="terminal-r1" x="48.8" y="190.8" textLength="24.4" clip-path="url(#terminal-line-7)">&#160;</text><text class="terminal-r4" x="73.2" y="190.8" textLength="109.8" clip-path="url(#terminal-line-7)">Shift+Tab</text><text class="terminal-r1" x="183" y="190.8" textLength="512.4" clip-path="url(#terminal-line-7)">&#160;Cycle&#160;through&#160;agents&#160;(default,&#160;plan,&#160;...)</text><text class="terminal-r1" x="1464" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">
</text><text class="terminal-r2" x="24.4" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)"></text><text class="terminal-r1" x="48.8" y="215.2" textLength="24.4" clip-path="url(#terminal-line-8)">&#160;</text><text class="terminal-r4" x="73.2" y="215.2" textLength="73.2" clip-path="url(#terminal-line-8)">Alt+↑↓</text><text class="terminal-r1" x="146.4" y="215.2" textLength="36.6" clip-path="url(#terminal-line-8)">&#160;/&#160;</text><text class="terminal-r4" x="183" y="215.2" textLength="97.6" clip-path="url(#terminal-line-8)">Ctrl+P/N</text><text class="terminal-r1" x="280.6" y="215.2" textLength="390.4" clip-path="url(#terminal-line-8)">&#160;Rewind&#160;to&#160;previous/next&#160;message</text><text class="terminal-r1" x="1464" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">
</text><text class="terminal-r2" x="24.4" y="239.6" textLength="12.2" clip-path="url(#terminal-line-9)"></text><text class="terminal-r1" x="1464" y="239.6" textLength="12.2" clip-path="url(#terminal-line-9)">
</text><text class="terminal-r2" x="24.4" y="264" textLength="12.2" clip-path="url(#terminal-line-10)"></text><text class="terminal-r3" x="48.8" y="264" textLength="195.2" clip-path="url(#terminal-line-10)">Special&#160;Features</text><text class="terminal-r1" x="1464" y="264" textLength="12.2" clip-path="url(#terminal-line-10)">
</text><text class="terminal-r2" x="24.4" y="288.4" textLength="12.2" clip-path="url(#terminal-line-11)"></text><text class="terminal-r1" x="1464" y="288.4" textLength="12.2" clip-path="url(#terminal-line-11)">
</text><text class="terminal-r2" x="24.4" y="312.8" textLength="12.2" clip-path="url(#terminal-line-12)"></text><text class="terminal-r1" x="48.8" y="312.8" textLength="24.4" clip-path="url(#terminal-line-12)">&#160;</text><text class="terminal-r4" x="73.2" y="312.8" textLength="122" clip-path="url(#terminal-line-12)">!&lt;command&gt;</text><text class="terminal-r1" x="195.2" y="312.8" textLength="366" clip-path="url(#terminal-line-12)">&#160;Execute&#160;bash&#160;command&#160;directly</text><text class="terminal-r1" x="1464" y="312.8" textLength="12.2" clip-path="url(#terminal-line-12)">
</text><text class="terminal-r2" x="24.4" y="337.2" textLength="12.2" clip-path="url(#terminal-line-13)"></text><text class="terminal-r1" x="48.8" y="337.2" textLength="24.4" clip-path="url(#terminal-line-13)">&#160;</text><text class="terminal-r4" x="73.2" y="337.2" textLength="170.8" clip-path="url(#terminal-line-13)">@path/to/file/</text><text class="terminal-r1" x="244" y="337.2" textLength="305" clip-path="url(#terminal-line-13)">&#160;Autocompletes&#160;file&#160;paths</text><text class="terminal-r1" x="1464" y="337.2" textLength="12.2" clip-path="url(#terminal-line-13)">
</text><text class="terminal-r2" x="24.4" y="361.6" textLength="12.2" clip-path="url(#terminal-line-14)"></text><text class="terminal-r1" x="1464" y="361.6" textLength="12.2" clip-path="url(#terminal-line-14)">
</text><text class="terminal-r2" x="24.4" y="386" textLength="12.2" clip-path="url(#terminal-line-15)"></text><text class="terminal-r3" x="48.8" y="386" textLength="97.6" clip-path="url(#terminal-line-15)">Commands</text><text class="terminal-r1" x="1464" y="386" textLength="12.2" clip-path="url(#terminal-line-15)">
</text><text class="terminal-r2" x="24.4" y="410.4" textLength="12.2" clip-path="url(#terminal-line-16)"></text><text class="terminal-r1" x="1464" y="410.4" textLength="12.2" clip-path="url(#terminal-line-16)">
</text><text class="terminal-r2" x="24.4" y="434.8" textLength="12.2" clip-path="url(#terminal-line-17)"></text><text class="terminal-r1" x="48.8" y="434.8" textLength="24.4" clip-path="url(#terminal-line-17)">&#160;</text><text class="terminal-r4" x="73.2" y="434.8" textLength="61" clip-path="url(#terminal-line-17)">/help</text><text class="terminal-r1" x="134.2" y="434.8" textLength="231.8" clip-path="url(#terminal-line-17)">:&#160;Show&#160;help&#160;message</text><text class="terminal-r1" x="1464" y="434.8" textLength="12.2" clip-path="url(#terminal-line-17)">
</text><text class="terminal-r2" x="24.4" y="459.2" textLength="12.2" clip-path="url(#terminal-line-18)"></text><text class="terminal-r1" x="48.8" y="459.2" textLength="24.4" clip-path="url(#terminal-line-18)">&#160;</text><text class="terminal-r4" x="73.2" y="459.2" textLength="85.4" clip-path="url(#terminal-line-18)">/config</text><text class="terminal-r1" x="158.6" y="459.2" textLength="268.4" clip-path="url(#terminal-line-18)">:&#160;Edit&#160;config&#160;settings</text><text class="terminal-r1" x="1464" y="459.2" textLength="12.2" clip-path="url(#terminal-line-18)">
</text><text class="terminal-r2" x="24.4" y="483.6" textLength="12.2" clip-path="url(#terminal-line-19)"></text><text class="terminal-r1" x="48.8" y="483.6" textLength="24.4" clip-path="url(#terminal-line-19)">&#160;</text><text class="terminal-r4" x="73.2" y="483.6" textLength="73.2" clip-path="url(#terminal-line-19)">/model</text><text class="terminal-r1" x="146.4" y="483.6" textLength="256.2" clip-path="url(#terminal-line-19)">:&#160;Select&#160;active&#160;model</text><text class="terminal-r1" x="1464" y="483.6" textLength="12.2" clip-path="url(#terminal-line-19)">
</text><text class="terminal-r2" x="24.4" y="508" textLength="12.2" clip-path="url(#terminal-line-20)"></text><text class="terminal-r1" x="48.8" y="508" textLength="24.4" clip-path="url(#terminal-line-20)">&#160;</text><text class="terminal-r4" x="73.2" y="508" textLength="109.8" clip-path="url(#terminal-line-20)">/thinking</text><text class="terminal-r1" x="183" y="508" textLength="280.6" clip-path="url(#terminal-line-20)">:&#160;Select&#160;thinking&#160;level</text><text class="terminal-r1" x="1464" y="508" textLength="12.2" clip-path="url(#terminal-line-20)">
</text><text class="terminal-r2" x="24.4" y="532.4" textLength="12.2" clip-path="url(#terminal-line-21)"></text><text class="terminal-r1" x="48.8" y="532.4" textLength="24.4" clip-path="url(#terminal-line-21)">&#160;</text><text class="terminal-r4" x="73.2" y="532.4" textLength="85.4" clip-path="url(#terminal-line-21)">/reload</text><text class="terminal-r1" x="158.6" y="532.4" textLength="780.8" clip-path="url(#terminal-line-21)">:&#160;Reload&#160;configuration,&#160;agent&#160;instructions,&#160;and&#160;skills&#160;from&#160;disk</text><text class="terminal-r1" x="1464" y="532.4" textLength="12.2" clip-path="url(#terminal-line-21)">
</text><text class="terminal-r2" x="24.4" y="556.8" textLength="12.2" clip-path="url(#terminal-line-22)"></text><text class="terminal-r1" x="48.8" y="556.8" textLength="24.4" clip-path="url(#terminal-line-22)">&#160;</text><text class="terminal-r4" x="73.2" y="556.8" textLength="73.2" clip-path="url(#terminal-line-22)">/clear</text><text class="terminal-r1" x="146.4" y="556.8" textLength="341.6" clip-path="url(#terminal-line-22)">:&#160;Clear&#160;conversation&#160;history</text><text class="terminal-r1" x="1464" y="556.8" textLength="12.2" clip-path="url(#terminal-line-22)">
</text><text class="terminal-r2" x="24.4" y="581.2" textLength="12.2" clip-path="url(#terminal-line-23)"></text><text class="terminal-r1" x="48.8" y="581.2" textLength="24.4" clip-path="url(#terminal-line-23)">&#160;</text><text class="terminal-r4" x="73.2" y="581.2" textLength="61" clip-path="url(#terminal-line-23)">/copy</text><text class="terminal-r1" x="134.2" y="581.2" textLength="561.2" clip-path="url(#terminal-line-23)">:&#160;Copy&#160;the&#160;last&#160;agent&#160;message&#160;to&#160;the&#160;clipboard</text><text class="terminal-r1" x="1464" y="581.2" textLength="12.2" clip-path="url(#terminal-line-23)">
</text><text class="terminal-r2" x="24.4" y="605.6" textLength="12.2" clip-path="url(#terminal-line-24)"></text><text class="terminal-r1" x="48.8" y="605.6" textLength="24.4" clip-path="url(#terminal-line-24)">&#160;</text><text class="terminal-r4" x="73.2" y="605.6" textLength="48.8" clip-path="url(#terminal-line-24)">/log</text><text class="terminal-r1" x="122" y="605.6" textLength="524.6" clip-path="url(#terminal-line-24)">:&#160;Show&#160;path&#160;to&#160;current&#160;interaction&#160;log&#160;file</text><text class="terminal-r1" x="1464" y="605.6" textLength="12.2" clip-path="url(#terminal-line-24)">
</text><text class="terminal-r2" x="24.4" y="630" textLength="12.2" clip-path="url(#terminal-line-25)"></text><text class="terminal-r1" x="48.8" y="630" textLength="24.4" clip-path="url(#terminal-line-25)">&#160;</text><text class="terminal-r4" x="73.2" y="630" textLength="73.2" clip-path="url(#terminal-line-25)">/debug</text><text class="terminal-r1" x="146.4" y="630" textLength="268.4" clip-path="url(#terminal-line-25)">:&#160;Toggle&#160;debug&#160;console</text><text class="terminal-r1" x="1464" y="630" textLength="12.2" clip-path="url(#terminal-line-25)">
</text><text class="terminal-r2" x="24.4" y="654.4" textLength="12.2" clip-path="url(#terminal-line-26)"></text><text class="terminal-r1" x="48.8" y="654.4" textLength="24.4" clip-path="url(#terminal-line-26)">&#160;</text><text class="terminal-r4" x="73.2" y="654.4" textLength="97.6" clip-path="url(#terminal-line-26)">/compact</text><text class="terminal-r1" x="170.8" y="654.4" textLength="1171.2" clip-path="url(#terminal-line-26)">:&#160;Compact&#160;conversation&#160;history&#160;by&#160;summarizing.&#160;Optionally&#160;pass&#160;instructions&#160;to&#160;guide&#160;the&#160;summary</text><text class="terminal-r1" x="1464" y="654.4" textLength="12.2" clip-path="url(#terminal-line-26)">
</text><text class="terminal-r2" x="24.4" y="678.8" textLength="12.2" clip-path="url(#terminal-line-27)"></text><text class="terminal-r1" x="48.8" y="678.8" textLength="24.4" clip-path="url(#terminal-line-27)">&#160;</text><text class="terminal-r4" x="73.2" y="678.8" textLength="61" clip-path="url(#terminal-line-27)">/exit</text><text class="terminal-r1" x="134.2" y="678.8" textLength="268.4" clip-path="url(#terminal-line-27)">:&#160;Exit&#160;the&#160;application</text><text class="terminal-r1" x="1464" y="678.8" textLength="12.2" clip-path="url(#terminal-line-27)">
</text><text class="terminal-r2" x="24.4" y="703.2" textLength="12.2" clip-path="url(#terminal-line-28)"></text><text class="terminal-r1" x="48.8" y="703.2" textLength="24.4" clip-path="url(#terminal-line-28)">&#160;</text><text class="terminal-r4" x="73.2" y="703.2" textLength="85.4" clip-path="url(#terminal-line-28)">/status</text><text class="terminal-r1" x="158.6" y="703.2" textLength="317.2" clip-path="url(#terminal-line-28)">:&#160;Display&#160;agent&#160;statistics</text><text class="terminal-r1" x="1464" y="703.2" textLength="12.2" clip-path="url(#terminal-line-28)">
</text><text class="terminal-r2" x="24.4" y="727.6" textLength="12.2" clip-path="url(#terminal-line-29)"></text><text class="terminal-r1" x="48.8" y="727.6" textLength="24.4" clip-path="url(#terminal-line-29)">&#160;</text><text class="terminal-r4" x="73.2" y="727.6" textLength="109.8" clip-path="url(#terminal-line-29)">/teleport</text><text class="terminal-r1" x="183" y="727.6" textLength="378.2" clip-path="url(#terminal-line-29)">:&#160;Teleport&#160;session&#160;to&#160;Vibe&#160;Code</text><text class="terminal-r1" x="1464" y="727.6" textLength="12.2" clip-path="url(#terminal-line-29)">
</text><text class="terminal-r2" x="24.4" y="752" textLength="12.2" clip-path="url(#terminal-line-30)"></text><text class="terminal-r1" x="48.8" y="752" textLength="24.4" clip-path="url(#terminal-line-30)">&#160;</text><text class="terminal-r4" x="73.2" y="752" textLength="146.4" clip-path="url(#terminal-line-30)">/proxy-setup</text><text class="terminal-r1" x="219.6" y="752" textLength="561.2" clip-path="url(#terminal-line-30)">:&#160;Configure&#160;proxy&#160;and&#160;SSL&#160;certificate&#160;settings</text><text class="terminal-r1" x="1464" y="752" textLength="12.2" clip-path="url(#terminal-line-30)">
</text><text class="terminal-r2" x="24.4" y="776.4" textLength="12.2" clip-path="url(#terminal-line-31)"></text><text class="terminal-r1" x="48.8" y="776.4" textLength="24.4" clip-path="url(#terminal-line-31)">&#160;</text><text class="terminal-r4" x="73.2" y="776.4" textLength="109.8" clip-path="url(#terminal-line-31)">/continue</text><text class="terminal-r1" x="183" y="776.4" textLength="24.4" clip-path="url(#terminal-line-31)">,&#160;</text><text class="terminal-r4" x="207.4" y="776.4" textLength="85.4" clip-path="url(#terminal-line-31)">/resume</text><text class="terminal-r1" x="292.8" y="776.4" textLength="402.6" clip-path="url(#terminal-line-31)">:&#160;Browse&#160;and&#160;resume&#160;past&#160;sessions</text><text class="terminal-r1" x="1464" y="776.4" textLength="12.2" clip-path="url(#terminal-line-31)">
</text><text class="terminal-r2" x="24.4" y="800.8" textLength="12.2" clip-path="url(#terminal-line-32)"></text><text class="terminal-r1" x="48.8" y="800.8" textLength="24.4" clip-path="url(#terminal-line-32)">&#160;</text><text class="terminal-r4" x="73.2" y="800.8" textLength="85.4" clip-path="url(#terminal-line-32)">/rename</text><text class="terminal-r1" x="158.6" y="800.8" textLength="341.6" clip-path="url(#terminal-line-32)">:&#160;Rename&#160;the&#160;current&#160;session</text><text class="terminal-r1" x="1464" y="800.8" textLength="12.2" clip-path="url(#terminal-line-32)">
</text><text class="terminal-r2" x="24.4" y="825.2" textLength="12.2" clip-path="url(#terminal-line-33)"></text><text class="terminal-r1" x="48.8" y="825.2" textLength="24.4" clip-path="url(#terminal-line-33)">&#160;</text><text class="terminal-r4" x="73.2" y="825.2" textLength="134.2" clip-path="url(#terminal-line-33)">/connectors</text><text class="terminal-r1" x="207.4" y="825.2" textLength="24.4" clip-path="url(#terminal-line-33)">,&#160;</text><text class="terminal-r4" x="231.8" y="825.2" textLength="48.8" clip-path="url(#terminal-line-33)">/mcp</text><text class="terminal-r1" x="280.6" y="825.2" textLength="939.4" clip-path="url(#terminal-line-33)">:&#160;Display&#160;available&#160;MCP&#160;servers&#160;and&#160;connectors.&#160;Pass&#160;a&#160;name&#160;to&#160;list&#160;its&#160;tools</text><text class="terminal-r1" x="1464" y="825.2" textLength="12.2" clip-path="url(#terminal-line-33)">
</text><text class="terminal-r2" x="24.4" y="849.6" textLength="12.2" clip-path="url(#terminal-line-34)"></text><text class="terminal-r1" x="48.8" y="849.6" textLength="24.4" clip-path="url(#terminal-line-34)">&#160;</text><text class="terminal-r4" x="73.2" y="849.6" textLength="73.2" clip-path="url(#terminal-line-34)">/voice</text><text class="terminal-r1" x="146.4" y="849.6" textLength="317.2" clip-path="url(#terminal-line-34)">:&#160;Configure&#160;voice&#160;settings</text><text class="terminal-r1" x="1464" y="849.6" textLength="12.2" clip-path="url(#terminal-line-34)">
</text><text class="terminal-r2" x="24.4" y="874" textLength="12.2" clip-path="url(#terminal-line-35)"></text><text class="terminal-r1" x="48.8" y="874" textLength="24.4" clip-path="url(#terminal-line-35)">&#160;</text><text class="terminal-r4" x="73.2" y="874" textLength="122" clip-path="url(#terminal-line-35)">/leanstall</text><text class="terminal-r1" x="195.2" y="874" textLength="463.6" clip-path="url(#terminal-line-35)">:&#160;Install&#160;the&#160;Lean&#160;4&#160;agent&#160;(leanstral)</text><text class="terminal-r1" x="1464" y="874" textLength="12.2" clip-path="url(#terminal-line-35)">
</text><text class="terminal-r2" x="24.4" y="898.4" textLength="12.2" clip-path="url(#terminal-line-36)"></text><text class="terminal-r1" x="48.8" y="898.4" textLength="24.4" clip-path="url(#terminal-line-36)">&#160;</text><text class="terminal-r4" x="73.2" y="898.4" textLength="146.4" clip-path="url(#terminal-line-36)">/unleanstall</text><text class="terminal-r1" x="219.6" y="898.4" textLength="341.6" clip-path="url(#terminal-line-36)">:&#160;Uninstall&#160;the&#160;Lean&#160;4&#160;agent</text><text class="terminal-r1" x="1464" y="898.4" textLength="12.2" clip-path="url(#terminal-line-36)">
</text><text class="terminal-r2" x="24.4" y="922.8" textLength="12.2" clip-path="url(#terminal-line-37)"></text><text class="terminal-r1" x="48.8" y="922.8" textLength="24.4" clip-path="url(#terminal-line-37)">&#160;</text><text class="terminal-r4" x="73.2" y="922.8" textLength="85.4" clip-path="url(#terminal-line-37)">/rewind</text><text class="terminal-r1" x="158.6" y="922.8" textLength="366" clip-path="url(#terminal-line-37)">:&#160;Rewind&#160;to&#160;a&#160;previous&#160;message</text><text class="terminal-r1" x="1464" y="922.8" textLength="12.2" clip-path="url(#terminal-line-37)">
</text><text class="terminal-r2" x="24.4" y="947.2" textLength="12.2" clip-path="url(#terminal-line-38)"></text><text class="terminal-r1" x="48.8" y="947.2" textLength="24.4" clip-path="url(#terminal-line-38)">&#160;</text><text class="terminal-r4" x="73.2" y="947.2" textLength="61" clip-path="url(#terminal-line-38)">/loop</text><text class="terminal-r1" x="134.2" y="947.2" textLength="427" clip-path="url(#terminal-line-38)">:&#160;Schedule&#160;a&#160;recurring&#160;prompt.&#160;Use&#160;</text><text class="terminal-r4" x="561.2" y="947.2" textLength="305" clip-path="url(#terminal-line-38)">/loop&#160;&lt;interval&gt;&#160;&lt;prompt&gt;</text><text class="terminal-r1" x="866.2" y="947.2" textLength="24.4" clip-path="url(#terminal-line-38)">,&#160;</text><text class="terminal-r4" x="890.6" y="947.2" textLength="122" clip-path="url(#terminal-line-38)">/loop&#160;list</text><text class="terminal-r1" x="1012.6" y="947.2" textLength="61" clip-path="url(#terminal-line-38)">,&#160;or&#160;</text><text class="terminal-r4" x="1073.6" y="947.2" textLength="256.2" clip-path="url(#terminal-line-38)">/loop&#160;cancel&#160;&lt;id|all&gt;</text><text class="terminal-r1" x="1464" y="947.2" textLength="12.2" clip-path="url(#terminal-line-38)">
</text><text class="terminal-r2" x="24.4" y="971.6" textLength="12.2" clip-path="url(#terminal-line-39)"></text><text class="terminal-r1" x="48.8" y="971.6" textLength="24.4" clip-path="url(#terminal-line-39)">&#160;</text><text class="terminal-r4" x="73.2" y="971.6" textLength="183" clip-path="url(#terminal-line-39)">/data-retention</text><text class="terminal-r1" x="256.2" y="971.6" textLength="402.6" clip-path="url(#terminal-line-39)">:&#160;Show&#160;data&#160;retention&#160;information</text><text class="terminal-r1" x="1464" y="971.6" textLength="12.2" clip-path="url(#terminal-line-39)">
</text><text class="terminal-r1" x="1464" y="996" textLength="12.2" clip-path="url(#terminal-line-40)">
</text><text class="terminal-r1" x="1464" y="1020.4" textLength="12.2" clip-path="url(#terminal-line-41)">

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Before After
Before After

View file

@ -11,6 +11,11 @@ def _pin_timezone(monkeypatch: pytest.MonkeyPatch) -> None:
time.tzset()
@pytest.fixture(autouse=True)
def _pin_snapshot_colors(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.delenv("NO_COLOR", raising=False)
@pytest.fixture(autouse=True)
def _pin_banner_version(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(

View file

@ -9,6 +9,7 @@ from tests.mock.utils import mock_llm_chunk
from tests.snapshots.base_snapshot_test_app import BaseSnapshotTestApp
from tests.snapshots.snap_compare import SnapCompare
from tests.stubs.fake_backend import FakeBackend
from vibe.core.telemetry.send import TelemetryClient
@pytest.fixture(autouse=True)
@ -20,6 +21,7 @@ def _enable_feedback_bar(monkeypatch: pytest.MonkeyPatch) -> None:
"vibe.cli.textual_ui.widgets.feedback_bar_manager.MIN_USER_MESSAGES_FOR_FEEDBACK",
1,
)
monkeypatch.setattr(TelemetryClient, "is_active", lambda self: True)
class FeedbackBarSnapshotApp(BaseSnapshotTestApp):

View file

@ -581,6 +581,60 @@ async def test_non_retryable_passes_through_non_streaming(observer_capture) -> N
assert agent.session_logger.save_interaction.await_count == 1
def _wrap_with_cause(message: str, cause: BaseException) -> RuntimeError:
# Mirrors how Temporal raises ``ActivityError`` on the workflow side with
# the original ``ApplicationError`` chained as ``__cause__`` — except we
# don't import temporalio. The wrap-site check has to traverse the chain
# to find ``non_retryable`` regardless of how deep it is.
error = RuntimeError(message)
error.__cause__ = cause
return error
@pytest.mark.asyncio
async def test_non_retryable_via_cause_chain_streaming(observer_capture) -> None:
observed, observer = observer_capture
wrapped = _wrap_with_cause(
"Activity task failed", _NonRetryableError("auth failed")
)
backend = FakeBackend(exception_to_raise=wrapped)
agent = build_test_agent_loop(
config=make_config(),
backend=backend,
message_observer=observer,
enable_streaming=True,
)
agent.session_logger.save_interaction = AsyncMock(return_value=None)
with pytest.raises(RuntimeError, match="Activity task failed"):
[_ async for _ in agent.act("Trigger non-retryable via cause chain")]
assert [role for role, _ in observed] == [Role.system, Role.user]
assert agent.session_logger.save_interaction.await_count == 1
@pytest.mark.asyncio
async def test_non_retryable_via_cause_chain_non_streaming(observer_capture) -> None:
observed, observer = observer_capture
wrapped = _wrap_with_cause(
"Activity task failed", _NonRetryableError("auth failed")
)
backend = FakeBackend(exception_to_raise=wrapped)
agent = build_test_agent_loop(
config=make_config(),
backend=backend,
message_observer=observer,
enable_streaming=False,
)
agent.session_logger.save_interaction = AsyncMock(return_value=None)
with pytest.raises(RuntimeError, match="Activity task failed"):
[_ async for _ in agent.act("Trigger non-retryable via cause chain")]
assert [role for role, _ in observed] == [Role.system, Role.user]
assert agent.session_logger.save_interaction.await_count == 1
def _snapshot_events(events: list) -> list[tuple[str, str]]:
return [
(type(e).__name__, e.content)

View file

@ -80,8 +80,15 @@ def test_run_programmatic_preload_streaming_is_batched(
new_session = [
e for e in telemetry_events if e.get("event_name") == "vibe.new_session"
]
assert len(new_session) == 0
session_closed = [
e for e in telemetry_events if e.get("event_name") == "vibe.session_closed"
]
assert len(session_closed) == 1
assert session_closed[0]["properties"]["agent_entrypoint"] == "programmatic"
assert (
spy.emitted[0][1] == "You are Vibe, a super useful programming assistant."
)

View file

@ -3,6 +3,8 @@ from __future__ import annotations
import json
from pathlib import Path
import pytest
from vibe.cli.history_manager import HistoryManager
@ -74,6 +76,45 @@ def test_history_manager_stores_slash_prefixed_entries(tmp_path: Path) -> None:
assert reloaded.get_previous(current_input="") is None
def test_history_manager_keeps_entries_when_reload_read_fails(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
history_file = tmp_path / "history.jsonl"
manager = HistoryManager(history_file)
manager.add("first")
def raise_os_error(*args: object, **kwargs: object) -> None:
raise OSError
monkeypatch.setattr("vibe.cli.history_manager.read_safe", raise_os_error)
manager.add("second")
assert manager.get_previous(current_input="") == "second"
assert manager.get_previous(current_input="") == "first"
def test_history_manager_resets_navigation_when_add_is_duplicate(
tmp_path: Path,
) -> None:
history_file = tmp_path / "history.jsonl"
manager = HistoryManager(history_file, max_entries=2)
manager.add("a")
manager.add("b")
other = HistoryManager(history_file, max_entries=10)
other.add("c")
other.add("d")
other.add("e")
assert manager.get_previous(current_input="") == "b"
manager.add("e")
assert manager.get_previous(current_input="") == "e"
assert manager.get_previous(current_input="") == "d"
assert manager.get_previous(current_input="") is None
def test_history_manager_allows_navigation_round_trip(tmp_path: Path) -> None:
history_file = tmp_path / "history.jsonl"
manager = HistoryManager(history_file)

View file

@ -48,19 +48,28 @@ def _otel_provider(monkeypatch: pytest.MonkeyPatch):
class TestSetupTracing:
def test_noop_when_disabled(self) -> None:
config = MagicMock(enable_otel=False)
config = MagicMock(enable_telemetry=True, enable_otel=False)
with patch("vibe.core.tracing.trace.set_tracer_provider") as mock_set:
setup_tracing(config)
mock_set.assert_not_called()
def test_noop_when_telemetry_disabled(self) -> None:
config = MagicMock(enable_telemetry=False, enable_otel=True)
with patch("vibe.core.tracing.trace.set_tracer_provider") as mock_set:
setup_tracing(config)
mock_set.assert_not_called()
def test_noop_when_exporter_config_is_none(self) -> None:
config = MagicMock(enable_otel=True, otel_span_exporter_config=None)
config = MagicMock(
enable_telemetry=True, enable_otel=True, otel_span_exporter_config=None
)
with patch("vibe.core.tracing.trace.set_tracer_provider") as mock_set:
setup_tracing(config)
mock_set.assert_not_called()
def test_configures_provider_from_exporter_config(self) -> None:
config = MagicMock(
enable_telemetry=True,
enable_otel=True,
otel_span_exporter_config=OtelSpanExporterConfig(
endpoint="https://customer.mistral.ai/telemetry/v1/traces",
@ -85,6 +94,7 @@ class TestSetupTracing:
def test_custom_endpoint_has_no_auth_headers(self) -> None:
config = MagicMock(
enable_telemetry=True,
enable_otel=True,
otel_span_exporter_config=OtelSpanExporterConfig(
endpoint="https://my-collector:4318/v1/traces"