From ea3708a98f338d667cc77e740738a2a5672e5da4 Mon Sep 17 00:00:00 2001 From: somew1nd Date: Mon, 24 Aug 2026 16:48:45 -0700 Subject: [PATCH] feat: implement robust multi-tier text input architecture and ADB diagnostics MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Android Text Input & IME Normalization: - Implement progressive 3-tier input architecture in AndroidAdbDriver and AndroidDeviceController: - Tier 1 (Primary): Zero-dependency clipboard injection + KEYCODE_PASTE (immune to IME Pinyin interception, full Unicode/Emoji support, preserves multiline formatting). - Tier 2 (Broadcast): Base64 broadcast injection when ADBKeyboard is active. - Tier 3 (Fallback): Native ADB physical keyevent fallback. - Add literal newline normalization (\r\n, \n, \r) to prevent JSON escape string corruption (e.g. '10086\n' -> '10086ใ€n'). - Add set_clipboard method to UIAutomatorClient without auxiliary APK requirements. - Diagnostics & System Probes: - Add adb_keys diagnostics probe for ADB key auth checks. - Integrate diagnostics into CLI doctor command. - Admin Console & Showcase UI: - Improve model service and system router in admin console. - Enhance workspace stream view and session state in showcase UI. --- apps/admin_console/index.html | 6 +- apps/admin_console/routers/system.py | 11 + apps/admin_console/services/model_service.py | 64 ++--- .../agent-stream/agent-stream.component.html | 6 +- .../agent-stream/agent-stream.component.ts | 15 +- .../src/app/core/models/session.model.ts | 1 + .../src/app/pages/home/home.component.html | 7 +- .../pages/workspace/workspace.component.html | 12 +- .../pages/workspace/workspace.component.ts | 2 +- artemis/clients/ui_automator_client.py | 17 ++ artemis/controllers/android_controller.py | 44 ++- artemis/core/diagnostics/__init__.py | 10 + artemis/core/diagnostics/adb_keys.py | 267 ++++++++++++++++++ artemis/core/diagnostics/engine.py | 19 +- artemis/core/diagnostics/probes/adb_probe.py | 44 +++ artemis/drivers/android/adb_driver.py | 49 ++-- artemis/interfaces/cli/commands/doctor.py | 49 +++- .../unit/admin_console/test_model_service.py | 53 ++++ tests/unit/core/test_adb_keys.py | 198 +++++++++++++ 19 files changed, 785 insertions(+), 89 deletions(-) create mode 100644 artemis/core/diagnostics/adb_keys.py create mode 100644 tests/unit/admin_console/test_model_service.py create mode 100644 tests/unit/core/test_adb_keys.py diff --git a/apps/admin_console/index.html b/apps/admin_console/index.html index aaac56e..d5157fb 100644 --- a/apps/admin_console/index.html +++ b/apps/admin_console/index.html @@ -241,10 +241,10 @@
- +
diff --git a/apps/admin_console/routers/system.py b/apps/admin_console/routers/system.py index 875dfd7..1859f7f 100644 --- a/apps/admin_console/routers/system.py +++ b/apps/admin_console/routers/system.py @@ -63,6 +63,17 @@ async def restart_adb_server(): } +@router.post("/adb/heal-keys") +async def heal_adb_keys(): + """Auto-heal corrupted ADB authentication RSA keys and return updated readiness.""" + heal_result = await readiness_engine.heal_adb_keys() + updated_report = await readiness_engine.run_all() + return { + "heal_result": heal_result, + "report": updated_report, + } + + class ConnectAdbRequest(BaseModel): """Payload to connect to an Android device over Wi-Fi.""" diff --git a/apps/admin_console/services/model_service.py b/apps/admin_console/services/model_service.py index b4d462f..41f30d5 100644 --- a/apps/admin_console/services/model_service.py +++ b/apps/admin_console/services/model_service.py @@ -17,26 +17,12 @@ from typing import Any class ModelService: - """Service handling model profile detection and metadata formatting.""" + """Service handling agent architecture profile detection and metadata formatting.""" @staticmethod def get_active_model_info(profile: str | None = None) -> dict[str, str]: - if profile: - p_lower = str(profile).lower() - if "pro" in p_lower: - return { - "name": "Pro", - "id": "gemini-3.7-pro", - "provider": "google", - } - elif "flash" in p_lower: - return { - "name": "Flash", - "id": "gemini-3.7-flash", - "provider": "google", - } - - model_name = "Flash" + """Return active architecture and underlying LLM model configuration.""" + # 1. Determine underlying LLM model and provider from config model_id = "gemini-3.7-flash" provider = "google" try: @@ -44,18 +30,31 @@ class ModelService: llm_cfg = parse_llm_config() if llm_cfg and llm_cfg.operator: - op_model = str(llm_cfg.operator.model).lower() provider = str(llm_cfg.operator.provider) - model_id = llm_cfg.operator.model - if "pro" in op_model: - model_name = "Pro" - elif "flash" in op_model: - model_name = "Flash" - else: - model_name = llm_cfg.operator.model.capitalize() + model_id = str(llm_cfg.operator.model) + elif llm_cfg and llm_cfg.default: + provider = str(llm_cfg.default.provider) + model_id = str(llm_cfg.default.model) except Exception: pass - return {"name": model_name, "id": model_id, "provider": provider} + + # 2. Determine agent architecture name (Flash vs Pro) + arch_name = "Flash" + if profile: + p_lower = str(profile).lower() + if "pro" in p_lower: + arch_name = "Pro" + elif "flash" in p_lower: + arch_name = "Flash" + else: + arch_name = str(profile).capitalize() + + return { + "name": arch_name, + "id": model_id, + "provider": provider, + "architecture": f"ARTEMIS {arch_name}", + } @staticmethod def resolve_session_profile( @@ -64,6 +63,7 @@ class ModelService: running_profile: str | None = None, agent_names: list[str] | None = None, ) -> str | None: + """Resolve the Artemis agent architecture profile ('flash' or 'pro') for a session.""" sess_profile = None d_info_raw = row_dict.get("device_info") if d_info_raw: @@ -98,7 +98,7 @@ class ModelService: if any("flashrunner" in name for name in agent_names_lower): return "flash" - # Check LLM traces for agent name or model overrides + # Check LLM traces for agent name if llm_trace_payloads: for tr_payload in llm_trace_payloads: if tr_payload: @@ -115,16 +115,6 @@ class ModelService: for x in ("planner", "validator", "operator", "checker") ): return "pro" - - m_str = str(p_obj.get("model") or p_obj.get("model_name") or "").lower() - if ( - "gemini-3.7-pro" in m_str - or "gemini-3.6-pro" in m_str - or "gemini-1.5-pro" in m_str - or "gpt-4" in m_str - or "claude" in m_str - ): - return "pro" except Exception: pass diff --git a/apps/showcase_ui/src/app/components/agent-stream/agent-stream.component.html b/apps/showcase_ui/src/app/components/agent-stream/agent-stream.component.html index 0a818ab..b2dc864 100644 --- a/apps/showcase_ui/src/app/components/agent-stream/agent-stream.component.html +++ b/apps/showcase_ui/src/app/components/agent-stream/agent-stream.component.html @@ -244,15 +244,15 @@
- +
{{ getModelIcon(model.name) }} - Model: + Architecture: {{ getModelDisplayName(model.name) }}
diff --git a/apps/showcase_ui/src/app/components/agent-stream/agent-stream.component.ts b/apps/showcase_ui/src/app/components/agent-stream/agent-stream.component.ts index deb9650..8adc308 100644 --- a/apps/showcase_ui/src/app/components/agent-stream/agent-stream.component.ts +++ b/apps/showcase_ui/src/app/components/agent-stream/agent-stream.component.ts @@ -20,7 +20,7 @@ import { FormsModule } from '@angular/forms'; import { RouterLink } from '@angular/router'; import { HttpClient } from '@angular/common/http'; import { AgentService, StartupProgressEvent } from '../../services/agent.service'; -import { Session } from '../../core/models/session.model'; +import { Session, ModelInfo } from '../../core/models/session.model'; import { MarkdownSegment, MarkdownLine, NoteMilestone, ParsedNote } from '../../core/models/markdown.model'; import { StepBlock, PhaseBlock, StepEvent, ActionParam, CheckerResult } from '../../core/models/stream.model'; @@ -1262,6 +1262,19 @@ export class AgentStreamComponent implements AfterViewInit { return ''; } + public getArchitectureTooltip(model?: ModelInfo | null): string { + if (!model) return 'Agent Architecture: ARTEMIS Flash (Reactive Fast Loop)'; + const name = this.getModelDisplayName(model.name); + const isPro = name.toLowerCase().includes('pro'); + const archDesc = isPro + ? 'ARTEMIS Pro (Multi-Agent Cognitive State Graph)' + : 'ARTEMIS Flash (Reactive Fast Loop)'; + if (model.id) { + return `Agent Architecture: ${archDesc} ยท LLM: ${model.id} (${model.provider || 'google'})`; + } + return `Agent Architecture: ${archDesc}`; + } + public formatTokenCount(tokens?: number): string { return formatTokenCount(tokens); } diff --git a/apps/showcase_ui/src/app/core/models/session.model.ts b/apps/showcase_ui/src/app/core/models/session.model.ts index b60e926..8a435bf 100644 --- a/apps/showcase_ui/src/app/core/models/session.model.ts +++ b/apps/showcase_ui/src/app/core/models/session.model.ts @@ -18,6 +18,7 @@ export interface ModelInfo { name: string; id: string; provider: string; + architecture?: string; } export interface TaskQueueItem { diff --git a/apps/showcase_ui/src/app/pages/home/home.component.html b/apps/showcase_ui/src/app/pages/home/home.component.html index d83f680..d49a831 100644 --- a/apps/showcase_ui/src/app/pages/home/home.component.html +++ b/apps/showcase_ui/src/app/pages/home/home.component.html @@ -1576,13 +1576,14 @@
- +
- + {{ item.profile === 'pro' ? 'Pro' : 'Flash' }}
diff --git a/apps/showcase_ui/src/app/pages/workspace/workspace.component.html b/apps/showcase_ui/src/app/pages/workspace/workspace.component.html index d7bf2c4..a9df9a5 100644 --- a/apps/showcase_ui/src/app/pages/workspace/workspace.component.html +++ b/apps/showcase_ui/src/app/pages/workspace/workspace.component.html @@ -103,8 +103,8 @@ rows="1" [disabled]="isSubmitting" [placeholder]="selectedProfile() === 'pro' - ? 'Pro: Best for complex long-running tasks, deep reasoning & video analysis' - : 'Flash: Fast execution for direct actions and tasks within 25 steps'" + ? 'Pro: Multi-agent cognitive graph architecture for complex multi-step tasks' + : 'Flash: Reactive fast loop for standard app workflows within 25 steps'" class="dock-textarea" spellcheck="false" > @@ -141,17 +141,17 @@