Skip to content

feat(ux): complete UX overhaul — design system, onboarding, web polish#1277

Merged
ilblackdragon merged 25 commits intostagingfrom
ux-overhaul
Mar 22, 2026
Merged

feat(ux): complete UX overhaul — design system, onboarding, web polish#1277
ilblackdragon merged 25 commits intostagingfrom
ux-overhaul

Conversation

@ilblackdragon
Copy link
Copy Markdown
Member

@ilblackdragon ilblackdragon commented Mar 17, 2026

Summary

Apple-level design refinements for the IronClaw web gateway and REPL — spring-physics motion, glass morphism depth, chat polish, mobile layout fixes, interactive REPL approval selector, and housekeeping cleanups.

Design System

  • Restored and extended design token system (--space-*, --text-*, --duration-*, --ease-*) with legacy aliases for theme backward compatibility
  • Added shadow tiers (--shadow-sm/md/lg), accent glow, glass morphism (--glass-bg/blur), spring easing tokens
  • All tokens defined in both dark (:root) and light ([data-theme="light"]) theme blocks

Micro-interactions

  • Spring-overshoot message entry animation, spring-scale button press
  • Tab crossfade, tool card smooth accordion (max-height transition)
  • Modal scale + blur entry, toast spring slide, sidebar width crossfade, card hover lift

Visual Depth

  • Tab bar glass morphism with sliding accent indicator
  • Assistant message accent left border, user message bubble tail
  • Floating chat input area (rounded, shadowed, margin-inset)

Chat Experience

  • Smooth streaming cursor, message hover timestamps, time separators
  • Textarea smooth auto-expand, send button glow when active

Settings & Forms

  • iOS-style toggle switches for boolean settings
  • Input focus glow, save feedback spring animation
  • Welcome card with gradient background and proper element spacing
  • Sticky settings group headers with glass backdrop

Mobile Layout (≤768px)

  • Sidebar toggle fix: toggleThreadSidebar() now uses expanded-mobile class on mobile instead of collapsed (desktop-only class that did nothing on mobile)
  • Sidebar overlay: backdrop overlay when expanded, auto-close on thread select or outside tap
  • Toggle button visibility: kept visible in collapsed 36px strip by hiding only header text/spacer, not the full header container
  • Settings drill-down: replaced cramped horizontal scrolling tabs with full-width category list → detail view → back button navigation
  • Tab bar polish: glass morphism background, top shadow, theme toggle hidden, tab indicator flipped to top edge
  • Thread list: fixed display: block (was display: flex causing horizontal layout)

REPL Improvements

  • Interactive approval selector: replaced ASCII-art box with clean horizontal rule card + inquire::Select picker (↑↓ + Enter), matching near-cli-rs style
  • stdin coordination: stdin_locked flag prevents readline from competing with the approval selector — locked on message send, unlocked on respond/selector completion
  • Transient status lines: Thinking and ToolStarted lines overwrite each other and are erased before final output (no clutter in scrollback)
  • Esc = deny: escaping the selector sends denial so the agent never gets stuck

Housekeeping

  • Widened TurnCost token fields from u32 to u64 to avoid truncation on large conversations
  • Removed unused _routine_engine_for_loop variable
  • Demoted noisy startup log messages (infodebug): builder, WASM tools, tunnel, WASM channels
  • Added cdn.jsdelivr.net and cdnjs.cloudflare.com to CSP connect-src for marked.js and DOMPurify

Accessibility & Mobile

  • Animated focus ring, prefers-reduced-motion global kill-switch
  • Touch targets ≥ 44px, mobile bottom-sheet modals, mobile bottom tab bar
  • Toast redesign: icon prefix, left accent border, countdown progress bar

Bug Fixes

  • Gateway/TEE popover z-index fixed (renders above tab panel content)
  • Connection lost banner renders as fixed top bar instead of broken flex child
  • Sidebar collapse preserves toggle + new thread buttons
  • Removed distracting green dot pulse animation on connected status
  • Deduplicated confirm-modal in HTML

Test plan

  • cargo check — compiles clean
  • cargo clippy --all --all-features — zero warnings
  • Open web UI, verify spring animations on messages, buttons, tabs
  • Toggle dark/light/system theme — all tokens render correctly
  • Test prefers-reduced-motion: reduce in DevTools — all animations disabled
  • Mobile (375px): tap » toggle → sidebar slides open as overlay with backdrop
  • Mobile: tap outside sidebar → dismisses; tap a thread → switches + dismisses
  • Mobile: Settings tab → category list shown, tap category → detail view, tap ← Back → returns to list
  • Mobile: bottom tab bar has glass morphism, tab indicator at top edge, theme toggle hidden
  • REPL: send message that triggers tool approval → interactive selector appears immediately
  • REPL: use ↑↓ to navigate, Enter to select; verify readline prompt not competing
  • REPL: press Esc in selector → denial sent, agent continues
  • REPL: thinking/tool-started lines overwrite each other, erased before response
  • Keyboard nav: tab switching, Enter send, Escape dismiss
  • SSE streaming: send a message, verify debounced rendering and tool cards
  • Safari: verify backdrop-filter and -webkit-backdrop-filter render

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings March 17, 2026 03:09
@github-actions github-actions bot added scope: agent Agent core (agent loop, router, scheduler) scope: channel Channel infrastructure scope: channel/cli TUI / CLI channel scope: channel/web Web gateway channel scope: channel/wasm WASM channel runtime scope: tool/builtin Built-in tools scope: llm LLM integration scope: workspace Persistent memory / workspace scope: config Configuration scope: setup Onboarding / setup scope: docs Documentation size: XL 500+ changed lines risk: high Safety, secrets, auth, or critical infrastructure contributor: core 20+ merged PRs labels Mar 17, 2026
@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request delivers a significant user experience upgrade across the application, focusing on consistency, ease of use, and intelligent personalization. It integrates new AI capabilities for task management and routine automation, while also bolstering security and improving the overall polish of both the command-line and web interfaces.

Highlights

  • Comprehensive UX Overhaul: Implemented a unified design system across CLI and web interfaces, including consistent accent colors, typography, spacing, and motion. This aims to provide a polished, calm, and intentional product experience.
  • Streamlined Onboarding Experience: The onboarding process has been significantly improved with auto-detection of API keys, the ability to skip interactive steps, modern branding, and a new --step flag for selective re-onboarding. A progressive disclosure boot screen now provides a concise overview.
  • Enhanced Web Chat Features: The web chat now includes a welcome card with suggestion chips, syntax highlighting for code blocks, user messages displayed as right-aligned chat bubbles, improved streaming polish, a connection indicator, and skeleton loading states for a smoother user experience.
  • New AI Assistant Capabilities: Introduced new skills for task delegation (delegation) and routine suggestions (routine-advisor), allowing the AI to help users break down tasks, set deadlines, track progress, and automate recurring activities.
  • Improved CLI Diagnostics and UI: The doctor command now groups diagnostics (Core/Features/External) and the REPL's /help command has been redesigned with a quick start section. Terminal output uses a new fmt module for consistent ANSI styling.
  • Psychographic Profile and Personalization: Added a new profile.rs module to define psychographic profile types and an analysis framework. This enables the AI to build and evolve a user's profile, generating personalized USER.md and assistant-directives.md documents.
  • Robust Prompt Injection Protection: Implemented prompt injection scanning for system-prompt-injected files (e.g., SOUL.md, USER.md) to prevent malicious overwrites and ensure the integrity of the agent's core instructions.
  • Accessibility and Internationalization: Enhanced accessibility with aria-live, aria-label, focus-visible, touch targets, and mobile optimization. All new strings have been added to English and Chinese (zh-CN) localization files.
Changelog
  • .env.example
    • Updated the default NEARAI_MODEL to a new value.
  • CLAUDE.md
    • Added profile.rs to the src/ directory description.
  • skills/delegation/SKILL.md
    • Added a new skill for task delegation, including activation keywords, patterns, and a detailed process for clarifying, breaking down, tracking, and executing tasks.
  • skills/routine-advisor/SKILL.md
    • Added a new skill for suggesting and creating cron routines based on user context and observed patterns, with guidelines for when and how to suggest routines.
  • src/agent/agent_loop.rs
    • Implemented a static bootstrap greeting (BOOTSTRAP_GREETING) that is persisted to the database and broadcast on first launch, ensuring immediate user feedback.
    • Added logic to emit per-turn cost summaries after agent execution.
    • Removed verbose debug logging for message processing and session resolution.
  • src/agent/dispatcher.rs
    • Updated status messages during LLM calls and tool execution to be more contextual, showing 'Thinking (step X)...' and specific tool names like 'Running command...'.
    • Added contextual_tool_message helper function to generate descriptive messages for single tool calls.
  • src/agent/routine.rs
    • Introduced normalize_cron_expression to convert 5-field and 6-field cron expressions to the 7-field format required by the cron crate.
    • Updated next_cron_fire to use the new cron normalization function, improving flexibility for schedule definitions.
    • Added new unit tests for cron expression normalization across different field counts.
  • src/agent/thread_ops.rs
    • Integrated logic to calculate and emit per-turn token usage and cost summaries via StatusUpdate::TurnCost.
  • src/app.rs
    • Added logic to check profile_onboarding_completed setting and mark the workspace's bootstrap as completed if already true, preventing re-injection of bootstrap instructions for existing users.
  • src/boot_screen.rs
    • Refactored the boot screen to display a more compact, three-tier status panel, focusing on essential information (name, version, model, backend, URLs).
    • Removed less critical details (database, tool count, features) from the main boot screen, directing users to ironclaw status for full details.
    • Added startup elapsed time to the boot screen footer.
  • src/channels/channel.rs
    • Added TurnCost variant to the StatusUpdate enum to convey per-turn token usage and cost information.
  • src/channels/repl.rs
    • Updated REPL UI to use the new crate::cli::fmt module for consistent ANSI styling, including hints, accents, and status indicators.
    • Improved the /help command output with a quick start section and grouped commands.
    • Implemented smart_truncate for tool parameter display, showing both ends of long strings.
    • Refined the appearance of approval cards and status updates with new ANSI color codes and symbols.
  • src/channels/wasm/wrapper.rs
    • Modified status_to_wit to explicitly skip StatusUpdate::TurnCost events for WASM channels, as they are web-gateway-only.
  • src/channels/web/mod.rs
    • Added active_config field to GatewayState to store a snapshot of the active configuration for the frontend status endpoint.
  • src/channels/web/server.rs
    • Defined ActiveConfigSnapshot struct to capture current LLM backend, model, and enabled channels.
    • Integrated ActiveConfigSnapshot into GatewayState and the GatewayStatusResponse for the web frontend.
  • src/channels/web/sse.rs
    • Added TurnCost as a new SseEvent type for broadcasting token usage and cost to connected clients.
  • src/channels/web/static/app.js
    • Implemented streaming debounce for assistant messages to improve UI responsiveness during long generations.
    • Added a connection status banner to inform users about disconnections and reconnections.
    • Introduced a send cooldown mechanism to prevent rapid message sending and handle rate limiting.
    • Added a welcome card with suggestion chips for new chat threads.
    • Integrated syntax highlighting for code blocks in chat messages using highlight.js.
    • Refactored tab switching to include a new 'Settings' tab with subtabs for Inference, Agent, Channels, Networking, Extensions, MCP, and Skills.
    • Implemented structured settings rendering with dynamic input types, descriptions, and restart indicators.
    • Updated extension and skill loading to use skeleton loaders and refresh based on the active settings subtab.
    • Replaced native window.confirm with a custom confirmation modal for removal actions.
    • Added keyboard shortcuts overlay and ARIA accessibility attributes for improved usability.
  • src/channels/web/static/i18n/en.js
    • Added new internationalization strings for the 'Settings' tab and its subtabs (Inference, Agent, Channels, Networking, MCP).
    • Included new strings for the welcome card, connection status, and structured settings elements.
    • Removed old 'tools' related strings, reflecting the consolidation into the 'Settings' tab.
  • src/channels/web/static/i18n/zh-CN.js
    • Added new internationalization strings for the 'Settings' tab and its subtabs (Inference, Agent, Channels, Networking, MCP) in Chinese.
    • Included new strings for the welcome card, connection status, and structured settings elements in Chinese.
    • Removed old 'tools' related strings, reflecting the consolidation into the 'Settings' tab.
  • src/channels/web/static/index.html
    • Integrated highlight.js for client-side syntax highlighting of code blocks.
    • Updated the authentication screen hint for clarity.
    • Modified the tab bar to include a new 'Settings' tab and removed 'Extensions' and 'Skills' as top-level tabs.
    • Added new HTML structure for settings subtabs, including search, export/import buttons, and structured configuration panels.
    • Implemented a custom confirmation modal to replace browser's native confirm dialog.
    • Added ARIA attributes to tab elements for improved accessibility.
  • src/channels/web/test_helpers.rs
    • Added active_config to the TestGatewayBuilder struct for test setup consistency.
  • src/channels/web/types.rs
    • Added TurnCost variant to the SseEvent enum for broadcasting token usage and cost data.
  • src/channels/web/ws.rs
    • Updated test setup to include active_config in GatewayState initialization.
  • src/cli/doctor.rs
    • Refactored the doctor command output to use the new crate::cli::fmt module for consistent ANSI styling.
    • Grouped diagnostic checks into 'Core', 'Features', and 'External' sections for better readability.
    • Improved the summary line with colored pass/fail/skip counts.
  • src/cli/fmt.rs
    • Added a new module fmt to centralize terminal styling, including color tokens (accent, bold, success, warning, error, dim, link, hint, reset), width detection, and rendering primitives (separators, key-value lines, status icons, box drawing, check lines).
    • Implemented logic to respect NO_COLOR environment variable and non-TTY output for color rendering.
  • src/cli/mod.rs
    • Added fmt module to the CLI module exports.
    • Introduced a new --step argument to the onboard command, allowing users to run specific setup steps (e.g., provider, channels).
  • src/cli/status.rs
    • Refactored the status command output to use the new crate::cli::fmt module for consistent and visually appealing presentation of system information.
  • src/config/llm.rs
    • Updated the default NEARAI_MODEL to use crate::llm::DEFAULT_MODEL for consistency.
  • src/error.rs
    • Added InjectionRejected variant to WorkspaceError to specifically handle cases where content is rejected due to prompt injection detection.
  • src/lib.rs
    • Added profile module to the library exports.
  • src/llm/mod.rs
    • Exported DEFAULT_MODEL and default_models from nearai_chat for broader use within the application.
  • src/llm/nearai_chat.rs
    • Defined DEFAULT_MODEL constant for the default NEAR AI model.
    • Provided default_models function to offer a fallback list of NEAR AI models for the setup wizard.
  • src/main.rs
    • Implemented format_top_level_error to provide colored and contextual error messages with recovery hints for common startup failures.
    • Added startup_start timestamp to measure and display the application's startup duration on the boot screen.
    • Ensured the WASM channels directory is created on startup, even if no channels are installed, to support hot-activation.
    • Passed an ActiveConfigSnapshot to the gateway channel, providing frontend with current LLM configuration and enabled channels.
  • src/profile.rs
    • Added a new module defining PsychographicProfile and its sub-components (PersonalityTraits, CommunicationPreferences, CohortClassification, etc.).
    • Included ANALYSIS_FRAMEWORK and PROFILE_JSON_SCHEMA constants to guide LLM-based profile generation and evolution.
    • Implemented is_populated, to_user_md, to_assistant_directives, and to_heartbeat_md methods for rendering profile data into various workspace documents.
    • Added custom deserializers for backward compatibility with older profile formats and robust handling of trait scores.
  • src/setup/README.md
    • Updated the setup README to clarify that personal onboarding now occurs conversationally with the running assistant, rather than as part of the initial wizard.
  • src/setup/mod.rs
    • Exported the new profile_evolution module.
    • Exported SetupError from the wizard module.
  • src/setup/profile_evolution.rs
    • Added a new module for generating LLM prompts to facilitate weekly psychographic profile evolution based on recent conversations.
    • Defined profile_evolution_prompt to instruct the LLM on how to update a profile given current data and conversation summaries.
    • Provided PROFILE_EVOLUTION_ROUTINE_PROMPT for the routine that triggers profile updates.
  • src/setup/prompts.rs
    • Updated the select_many prompt UI with new checkbox styling (green checkmark for selected, dim dot for unselected).
    • Replaced the ASCII art banner with a minimal wordmark banner (ironclaw).
    • Modified print_step to use a dot-based indicator (● ● ◉ ○ ○) for a more modern and compact visual representation of progress.
  • src/setup/wizard.rs
    • Implemented the --step flag for the onboard command, allowing users to run specific setup steps (e.g., ironclaw onboard --step provider,model).
    • Added auto-detection of API keys from environment variables for NEAR AI, Anthropic, OpenAI, and OpenRouter, allowing the wizard to skip interactive prompts if credentials are found.
    • Updated NEAR AI model selection to use crate::llm::default_models as a fallback if the API is unreachable.
    • Refactored the final summary output to use the new crate::cli::fmt module, presenting a concise completion card with key facts and action hints.
  • src/tools/builtin/memory.rs
    • Removed the PROTECTED_IDENTITY_FILES constant, as prompt injection protection is now handled generically by the workspace module.
    • Added map_write_err to convert WorkspaceError into ToolError, specifically mapping InjectionRejected to ToolError::NotAuthorized.
    • Modified memory_write to call workspace.mark_bootstrap_completed() and workspace.sync_profile_documents() when context/profile.json is written, automating profile-related document updates.
  • src/tools/builtin/routine.rs
    • Updated RoutineCreateTool and RoutineUpdateTool to utilize the new normalize_cron_expression function, ensuring compatibility with 5-field and 6-field cron schedules.
  • src/workspace/document.rs
    • Added new constants PROFILE (context/profile.json) and ASSISTANT_DIRECTIVES (context/assistant-directives.md) to paths for psychographic profile and derived behavioral directives.
  • src/workspace/mod.rs
    • Introduced SYSTEM_PROMPT_FILES constant to list files whose writes are subject to prompt injection scanning.
    • Implemented is_system_prompt_file to check if a path corresponds to a system-prompt-injected file.
    • Added SANITIZER static lazy-loaded instance and reject_if_injected function to perform prompt injection detection on writes to sensitive files.
    • Added bootstrap_pending and bootstrap_completed atomic flags to manage the first-run bootstrap greeting and its suppression.
    • Implemented sync_profile_documents to automatically generate and merge USER.md, context/assistant-directives.md, and HEARTBEAT.md based on the context/profile.json content.
    • Updated system_prompt_for_context to dynamically inject profile personalization (Tier 1 summary, Tier 2 full context) and the profile JSON schema during bootstrap onboarding.
    • Modified seed_if_empty to use new include_str! for seed files and to conditionally seed BOOTSTRAP.md only if the workspace is fresh and no profile exists, preventing redundant onboarding.
  • src/workspace/seeds/AGENTS.md
    • Updated content to include guidelines for passive profile building and managing identity files (USER.md, IDENTITY.md).
  • src/workspace/seeds/BOOTSTRAP.md
    • Revised content for the first-run bootstrap ritual, focusing on greeting, natural user learning, mandatory profile saving, and style guidelines.
  • src/workspace/seeds/GREETING.md
    • Added a new static greeting message for the initial user interaction.
  • src/workspace/seeds/HEARTBEAT.md
    • Updated content for the heartbeat checklist seed file.
  • src/workspace/seeds/IDENTITY.md
    • Updated content for the agent's identity seed file.
  • src/workspace/seeds/MEMORY.md
    • Updated content for the memory guidelines seed file.
  • src/workspace/seeds/README.md
    • Updated content for the workspace overview seed file.
  • src/workspace/seeds/SOUL.md
    • Updated content for core values, boundaries, and autonomy guidelines.
  • src/workspace/seeds/TOOLS.md
    • Updated content for environment-specific tool notes seed file.
  • src/workspace/seeds/USER.md
    • Updated content for the user context seed file.
  • tests/e2e/helpers.py
    • Updated UI selectors to reflect the new settings subtab structure and the custom confirmation modal.
    • Modified the TABS list to include 'settings' instead of 'extensions' and 'skills' as top-level tabs.
  • tests/e2e/scenarios/test_extensions.py
    • Adapted E2E tests for extensions to navigate through the new 'Settings' tab and its 'Extensions' subtab.
    • Removed tests related to the dedicated 'tools' table, as tools are now managed within extensions.
    • Updated tests for MCP servers to use the new 'MCP' settings subtab.
    • Modified tests to interact with the new custom confirmation modal instead of window.confirm.
  • tests/e2e/scenarios/test_skills.py
    • Adjusted E2E tests for skills to navigate through the new 'Settings' tab and its 'Skills' subtab.
    • Updated tests to interact with the new custom confirmation modal for skill removal.
  • tests/e2e/scenarios/test_wasm_lifecycle.py
    • Updated E2E tests for WASM lifecycle to reflect navigation through the new 'Settings' tab and its 'Extensions' subtab.
  • tests/e2e_advanced_traces.rs
    • Added a new test case (bootstrap_greeting_fires) to verify that a fresh workspace triggers the static bootstrap greeting before any user message or LLM interaction.
  • tests/e2e_builtin_tool_coverage.rs
    • Updated cron schedule assertion to match the new 7-field normalized format.
  • tests/openai_compat_integration.rs
    • Added active_config to GatewayState initialization in test setups for consistency.
  • tests/support/gateway_workflow_harness.rs
    • Included active_config in GatewayState initialization within the test harness.
  • tests/support/test_channel.rs
    • Added channel_name field to TestChannel and a with_name method to allow custom channel naming in tests.
    • Updated send_message and send_message_in_thread to use the configurable channel name.
  • tests/support/test_rig.rs
    • Introduced keep_bootstrap flag to TestRigBuilder to control whether the bootstrap pending flag is cleared, enabling tests for the proactive greeting.
    • Modified TestRigBuilder::build to conditionally name the test channel 'gateway' when keep_bootstrap is true, ensuring the bootstrap greeting is received.
  • tests/ws_gateway_integration.rs
    • Added active_config to GatewayState initialization in test setups.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Comprehensive UX overhaul spanning CLI and web surfaces, plus new “conversational onboarding” via workspace bootstrap/profile files. The PR also adds safety scanning for system-prompt-injected workspace documents and expands gateway status payloads for frontend display.

Changes:

  • Introduces a shared CLI formatting “design system” (cli::fmt) and refreshes boot/status/doctor/REPL output accordingly.
  • Adds workspace seed templates + bootstrap greeting/onboarding flow, psychographic profile scaffolding, and prompt-injection scanning for system-prompt-injected workspace files.
  • Polishes web gateway UI structure (Settings tab + subtabs), adds SSE/WS event types (turn cost), and updates E2E/integration tests to match.

Reviewed changes

Copilot reviewed 65 out of 66 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
tests/ws_gateway_integration.rs Updates test gateway state construction to include active_config.
tests/support/test_rig.rs Adds bootstrap-aware test rig behavior (optionally preserve bootstrap + ensure gateway channel name).
tests/support/test_channel.rs Makes test channel name configurable (needed for bootstrap tests).
tests/support/gateway_workflow_harness.rs Updates harness gateway state construction to include active_config.
tests/openai_compat_integration.rs Updates test gateway state construction to include active_config.
tests/e2e_builtin_tool_coverage.rs Updates expected cron expression to normalized 7-field format.
tests/e2e_advanced_traces.rs Adds E2E test asserting bootstrap greeting fires on fresh workspace.
tests/e2e/scenarios/test_wasm_lifecycle.py Updates navigation to match new Settings + Extensions subtab layout.
tests/e2e/scenarios/test_skills.py Updates navigation to Settings > Skills and adapts removal confirmation flow.
tests/e2e/scenarios/test_extensions.py Refactors tests for new Settings subtabs (Extensions/MCP/Channels) and custom confirm modal.
tests/e2e/helpers.py Adds selectors for Settings subtabs/panels + confirm modal; updates tab list.
src/workspace/seeds/USER.md New seed template for user context.
src/workspace/seeds/TOOLS.md New seed template for environment-specific tool notes.
src/workspace/seeds/SOUL.md New seed template for assistant “values/boundaries”.
src/workspace/seeds/README.md New seed template documenting workspace structure.
src/workspace/seeds/MEMORY.md New seed template for long-term memory file.
src/workspace/seeds/IDENTITY.md New seed template for assistant identity.
src/workspace/seeds/HEARTBEAT.md New seed template for heartbeat checklist.
src/workspace/seeds/GREETING.md Adds static first-run greeting content persisted/broadcast on bootstrap.
src/workspace/seeds/BOOTSTRAP.md Adds detailed first-run bootstrap instructions for conversational onboarding.
src/workspace/seeds/AGENTS.md New seed template for session routines/operational instructions.
src/workspace/mod.rs Adds system-prompt file injection scanning, bootstrap flags, profile injection logic, and profile→document sync.
src/workspace/document.rs Adds canonical paths for context/profile.json and context/assistant-directives.md.
src/tools/builtin/routine.rs Normalizes cron expressions to 7-field format before validation/storage.
src/tools/builtin/memory.rs Routes injection rejections to NotAuthorized, marks bootstrap completed, and syncs derived docs on profile write.
src/setup/wizard.rs Adds --step selective onboarding, provider autodetect from env, and refreshes completion summary UX.
src/setup/prompts.rs Updates interactive prompt rendering (step indicator, banner, checkbox UI).
src/setup/profile_evolution.rs New module to generate weekly profile-evolution prompts + routine prompt template.
src/setup/mod.rs Exposes SetupError, exports profile_evolution, and documents conversational onboarding.
src/setup/README.md Documents that personal onboarding happens via workspace bootstrap in the running assistant.
src/settings.rs Adds profile_onboarding_completed persisted flag (alias supported).
src/main.rs Adds top-level error formatting with hints, startup timing, ensures WASM channels dir exists, and publishes active config snapshot.
src/llm/nearai_chat.rs Updates NEAR AI default model and provides fallback model list for setup wizard.
src/llm/mod.rs Re-exports DEFAULT_MODEL and default_models().
src/lib.rs Exposes new profile module.
src/error.rs Adds WorkspaceError::InjectionRejected.
src/config/llm.rs Updates default NEAR AI model to crate::llm::DEFAULT_MODEL.
src/cli/status.rs Switches status output to cli::fmt key/value lines and updated layout.
src/cli/mod.rs Exposes cli::fmt and adds onboard --step flag (deprecates older single-step flags).
src/cli/fmt.rs New shared terminal formatting/color token module with NO_COLOR + TTY detection.
src/cli/doctor.rs Groups diagnostics and uses cli::fmt for consistent rendering.
src/channels/web/ws.rs Updates test gateway state construction to include active_config.
src/channels/web/types.rs Adds SSE event turn_cost and wires event typing for WS/SSE payloads.
src/channels/web/test_helpers.rs Updates helper gateway state construction to include active_config.
src/channels/web/static/index.html Adds Settings tab/subtabs layout, confirm modal, aria attributes, and Highlight.js assets.
src/channels/web/static/i18n/zh-CN.js Adds Settings and new UI strings, updates extensions wording.
src/channels/web/static/i18n/en.js Adds Settings and new UI strings, updates extensions wording.
src/channels/web/sse.rs Wires SSE event type mapping for turn_cost.
src/channels/web/server.rs Adds ActiveConfigSnapshot and returns active config fields in /api/gateway/status.
src/channels/web/mod.rs Adds with_active_config and maps StatusUpdate::TurnCost → SSE event.
src/channels/wasm/wrapper.rs Explicitly skips web-only status updates (suggestions + turn cost) for WASM channels.
src/channels/repl.rs Migrates REPL output to cli::fmt, improves truncation, and refreshes /help and approval rendering.
src/channels/channel.rs Adds StatusUpdate::TurnCost.
src/boot_screen.rs Reworks boot screen to a 2-tier summary and links to ironclaw status for full details.
src/app.rs Loads profile_onboarding_completed from settings to suppress bootstrap injection for existing users.
src/agent/thread_ops.rs Emits StatusUpdate::TurnCost after a response.
src/agent/routine.rs Adds cron normalization and updates cron parsing to accept 5/6/7-field expressions.
src/agent/dispatcher.rs Improves thinking/status messages with tool-name-aware wording.
src/agent/agent_loop.rs Adds bootstrap greeting persistence + broadcast flow driven by workspace bootstrap flag.
skills/routine-advisor/SKILL.md New skill to suggest routines based on user patterns + cron examples.
skills/delegation/SKILL.md New skill to guide task delegation + persistence via memory/routines.
CLAUDE.md Updates repo map documentation to include new profile.rs.
.env.example Updates example NEAR AI model default.

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

Comment on lines +492 to +516
// Emit per-turn cost summary
{
let usage = self.cost_guard().model_usage().await;
let (total_in, total_out, total_cost) =
usage
.values()
.fold((0u64, 0u64, rust_decimal::Decimal::ZERO), |acc, m| {
(
acc.0 + m.input_tokens,
acc.1 + m.output_tokens,
acc.2 + m.cost,
)
});
let _ = self
.channels
.send_status(
&message.channel,
StatusUpdate::TurnCost {
input_tokens: total_in as u32,
output_tokens: total_out as u32,
cost_usd: format!("${:.4}", total_cost),
},
&message.metadata,
)
.await;
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The u32→u64 overflow concern is fixed in this PR. The cumulative-vs-per-turn design issue is a pre-existing limitation of the CostGuard API — tracking per-turn deltas requires snapshotting before/after, which is a separate change. Filed for follow-up.

Comment on lines +553 to +565
// Top border: ┌ tool_name requires approval ───
let top_label = format!(" {tool_name} requires approval ");
let top_fill = box_width.saturating_sub(top_label.len() + 1);
let top_border = format!(
"\u{250C}\x1b[33m{top_label}\x1b[0m{}",
"\u{250C}{}{top_label}{}{}",
fmt::warning(),
fmt::reset(),
"\u{2500}".repeat(top_fill)
);

// Bottom border: └─ short_id ─────
let bot_label = format!(" {short_id} ");
let bot_fill = box_width.saturating_sub(bot_label.len() + 2);
let bot_border = format!(
"\u{2514}\u{2500}\x1b[90m{bot_label}\x1b[0m{}",
"\u{2500}".repeat(bot_fill)
);
// Bottom border: └─────
let bot_fill = box_width.saturating_sub(1);
let bot_border = format!("\u{2514}{}", "\u{2500}".repeat(bot_fill));
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed — the ASCII box was replaced entirely with a clean horizontal-rule card plus an inquire::Select interactive selector in commit 61d3ba0.

Comment on lines +15 to +20
/// - stdout is not a terminal (pipe, file redirect, CI)
fn colors_enabled() -> bool {
if std::env::var_os("NO_COLOR").is_some() {
return false;
}
std::io::stdout().is_terminal()
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Valid. The suggested fix (check both stdout and stderr) is reasonable. In practice, the most common redirect scenario is piping stdout while keeping stderr on a TTY, so checking either would be an improvement. Will address in a follow-up.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

The pull request includes several changes to improve the IronClaw agent. The default NEARAI_MODEL in .env.example was updated to Qwen/Qwen3.5-122B-A10B. A new profile.rs file was added to the src/ directory for psychographic profile types. The delegation and routine-advisor skills were added. A bootstrap greeting was added to the agent loop. The agent dispatcher was updated to provide more contextual thinking messages based on tool names. The routine advisor was updated to normalize cron expressions. The boot screen was updated to be more compact. The REPL channel was updated to use a new color scheme and to truncate long strings. The web gateway channel was updated to include per-turn cost summaries. The web gateway was updated to include a snapshot of the active configuration. The web gateway's javascript was updated to include a number of new features, including a settings tab, a confirmation modal, and a welcome card. A number of other changes were made to improve the agent's performance and stability. The code reviewer suggested that the errors from get_or_create_assistant_conversation and persist_assistant_response should be handled to prevent silent failures. The code reviewer also suggested that the current implementation of normalize_cron_expression for 5-field expressions might be subtly incorrect and that a more robust approach would be to join the fields collected from split_whitespace. The code reviewer also suggested that the logic for handling the bootstrap process can be simplified.

Comment on lines +273 to +281
let thread_id = store
.get_or_create_assistant_conversation("default", "gateway")
.await
.ok();
if let Some(id) = thread_id {
self.persist_assistant_response(id, "gateway", "default", BOOTSTRAP_GREETING)
.await;
}
thread_id
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The errors from get_or_create_assistant_conversation and persist_assistant_response are being ignored. This can lead to silent failures where the bootstrap greeting is not persisted to the database, which could be confusing for new users. It would be more robust to handle these potential errors, for example by logging them. This aligns with the principle of robust error handling and logging, as seen in the rule regarding tokio::task::spawn_blocking.

                let thread_id = match store
                    .get_or_create_assistant_conversation("default", "gateway")
                    .await
                {
                    Ok(id) => {
                        if let Err(e) = self
                            .persist_assistant_response(id, "gateway", "default", BOOTSTRAP_GREETING)
                            .await
                        {
                            tracing::warn!("Failed to persist bootstrap greeting: {}", e);
                        }
                        Some(id)
                    }
                    Err(e) => {
                        tracing::warn!("Failed to get or create bootstrap conversation: {}", e);
                        None
                    }
                };
References
  1. When handling errors, log them to capture debugging information and prevent silent failures. It is good practice to distinguish between different error types in the message.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Good suggestion. The bootstrap greeting is best-effort (non-critical path), so silent failure is intentional — but logging a warning is better than silently dropping. Will add tracing::warn in a follow-up.

pub fn normalize_cron_expression(schedule: &str) -> String {
let fields: Vec<&str> = schedule.split_whitespace().collect();
match fields.len() {
5 => format!("0 {} *", schedule.trim()),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The current implementation of normalize_cron_expression for 5-field expressions might be subtly incorrect. Using format!("0 {} *", schedule.trim()) could lead to issues if schedule.trim() itself contains extra spaces that are not handled by split_whitespace. A more robust approach would be to join the fields collected from split_whitespace.

Suggested change
5 => format!("0 {} *", schedule.trim()),
5 => format!("0 {} *", fields.join(" ")),

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Good catch — fields.join(" ") is more robust than re-trimming. Applied suggestion.

Comment on lines +699 to +718
let bootstrap_injected = if self.is_bootstrap_completed() {
if self
.read(paths::BOOTSTRAP)
.await
.is_ok_and(|d| !d.content.is_empty())
{
tracing::warn!(
"BOOTSTRAP.md still exists but profile_onboarding_completed is set; \
suppressing bootstrap injection"
);
}
false
} else if let Ok(doc) = self.read(paths::BOOTSTRAP).await
&& !doc.content.is_empty()
{
parts.push(format!(
"## First-Run Bootstrap\n\n\
A BOOTSTRAP.md file exists in the workspace. Read and follow it, \
then delete it when done.\n\n{}",
doc.content
));
}
parts.push(format!("## First-Run Bootstrap\n\n{}", doc.content));
true
} else {
false
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The logic for handling the bootstrap process can be simplified. Instead of checking is_bootstrap_completed and then reading the file, you can combine these checks for better readability and to avoid nested if statements.

Suggested change
let bootstrap_injected = if self.is_bootstrap_completed() {
if self
.read(paths::BOOTSTRAP)
.await
.is_ok_and(|d| !d.content.is_empty())
{
tracing::warn!(
"BOOTSTRAP.md still exists but profile_onboarding_completed is set; \
suppressing bootstrap injection"
);
}
false
} else if let Ok(doc) = self.read(paths::BOOTSTRAP).await
&& !doc.content.is_empty()
{
parts.push(format!(
"## First-Run Bootstrap\n\n\
A BOOTSTRAP.md file exists in the workspace. Read and follow it, \
then delete it when done.\n\n{}",
doc.content
));
}
parts.push(format!("## First-Run Bootstrap\n\n{}", doc.content));
true
} else {
false
};
let bootstrap_injected = if self.is_bootstrap_completed() {
if self.read(paths::BOOTSTRAP).await.is_ok_and(|d| !d.content.is_empty()) {
tracing::warn!(
"BOOTSTRAP.md still exists but profile_onboarding_completed is set; suppressing bootstrap injection"
);
}
false
} else if let Ok(doc) = self.read(paths::BOOTSTRAP).await && !doc.content.is_empty() {
parts.push(format!("## First-Run Bootstrap\n\n{}", doc.content));
true
} else {
false
};

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The suggested refactoring uses let-chains (if let Ok(doc) = ... && !doc.content.is_empty()) which requires a newer Rust edition. The current nested form is equivalent and works on stable. Will revisit when we bump MSRV.

Copy link
Copy Markdown
Collaborator

@zmanian zmanian left a comment

Choose a reason for hiding this comment

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

Review: massive UX overhaul + psychographic profiling + identity file safety

This is a 36-commit, 7300-line PR spanning psychographic profiling, identity file injection scanning, bootstrap/onboarding redesign, CLI design system, web UI overhaul, i18n, new skills, and new SSE events. Observations below.

Critical: CI did not run (fork PR)

Only classify/scope ran. No Clippy (any feature combo), formatting, panic-check, or regression enforcement. For a PR touching 66 files across Rust, JS, CSS, and HTML, full CI is mandatory before merge. Either push a maintainer commit to the branch or run local CI.

Concerning: scope should be split

This combines at least 4 orthogonal features:

  1. Psychographic profiling (profile.rs, onboarding_chat.rs, profile_evolution.rs, seeds)
  2. Identity file write protection (memory.rs -> workspace injection scanning)
  3. Web UI overhaul (CSS/JS/HTML, i18n, settings tabs)
  4. CLI design system (fmt.rs, boot screen, REPL, doctor/status)

Each would benefit from independent review and rollback capability. I'd strongly suggest splitting this so each feature can land and be tested independently.

Concerning: [skip-regression-check] on functional commits

Commits 35 and 36 both use [skip-regression-check] but contain substantial functional changes (SSE event handling, streaming debounce changes, boot screen restructuring, connection status logic). These aren't pure style changes.

Concerning: highlight.js CDN dependency

Commit 36 introduces syntax highlighting via a CDN. External CDN dependencies create availability risks (offline users, CDN outages) and supply chain risk (CDN compromise). Consider bundling or using a self-hosted copy.

Concerning: profile types use String instead of enums

CommunicationPreferences fields (detail_level, formality, tone, etc.) are all String. Per project conventions ("prefer strong types over strings"), these should be enums. This makes invalid states representable and means deserialization won't catch typos.

Positives:

  • Identity file injection scanning evolution is good -- moving from tool layer (memory.rs) to Workspace::write/append (commit 20) ensures all write paths are protected
  • LazyLock for the sanitizer (commit 33) avoids rebuilding Aho-Corasick on every write
  • The non-empty profile check for bootstrap suppression is a solid edge case fix
  • CSP-compliant event handlers (commit 7) is a good security improvement
  • The routine_trigger_message Cow pattern is efficient

Minor notes:

  • deserialize_trait_score in profile.rs silently returns 50 on deser failure -- intentional for LLM robustness but worth a comment
  • StatusUpdate::TurnCost -- verify the enum variant definition is included in this diff (I didn't find it in the patch)
  • The env_or_override fix for NEARAI_API_KEY (commit 5) is a standalone bug fix that should land independently

Copy link
Copy Markdown
Collaborator

@zmanian zmanian left a comment

Choose a reason for hiding this comment

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

Updated Review: UX overhaul + psychographic profiling + identity file safety

Updating my earlier review -- scope is fine as a single PR given the interconnected nature of the changes.

Critical: CI did not run (fork PR)

Only classify/scope ran. No Clippy (any feature combo), formatting, panic-check, or regression enforcement. For a PR touching 66 files across Rust, JS, CSS, and HTML, full CI is mandatory before merge. Either push a maintainer commit to the branch or run local CI.

Concerning: [skip-regression-check] on functional commits

Commits 35 and 36 both use [skip-regression-check] but contain substantial functional changes (SSE event handling, streaming debounce changes, boot screen restructuring, connection status logic). These aren't pure style changes.

Concerning: highlight.js CDN dependency

Commit 36 introduces syntax highlighting via a CDN. External CDN dependencies create availability risks (offline users, CDN outages) and supply chain risk (CDN compromise). Consider bundling or using a self-hosted copy.

Concerning: profile types use String instead of enums

CommunicationPreferences fields (detail_level, formality, tone, etc.) are all String. Per project conventions ("prefer strong types over strings"), these should be enums. This makes invalid states representable and means deserialization won't catch typos.

Positives:

  • Identity file injection scanning evolution is good -- moving from tool layer (memory.rs) to Workspace::write/append (commit 20) ensures all write paths are protected
  • LazyLock for the sanitizer (commit 33) avoids rebuilding Aho-Corasick on every write
  • The non-empty profile check for bootstrap suppression is a solid edge case fix
  • CSP-compliant event handlers (commit 7) is a good security improvement
  • The routine_trigger_message Cow pattern is efficient
  • The env_or_override fix for NEARAI_API_KEY (commit 5) is a nice catch

Minor notes:

  • deserialize_trait_score in profile.rs silently returns 50 on deser failure -- intentional for LLM robustness but worth a comment
  • StatusUpdate::TurnCost -- verify the enum variant definition is included in this diff (I didn't find it in the patch)

@ilblackdragon ilblackdragon marked this pull request as draft March 17, 2026 04:01
@ilblackdragon
Copy link
Copy Markdown
Member Author

This is still a draft and waiting for #927 and #1191 to merge (this merged them internal)

@github-actions github-actions bot added scope: tool Tool infrastructure scope: db Database trait / abstraction scope: db/libsql libSQL / Turso backend scope: orchestrator Container orchestrator scope: worker Container worker labels Mar 20, 2026
ilblackdragon and others added 2 commits March 21, 2026 14:37
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove failed message before retry to prevent duplicate user messages
- Revert connect-src to 'self' — CDN hosts only need script-src
- Use red for Deny confirmation in REPL approval selector

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings March 21, 2026 21:55
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

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


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

Comment on lines +48 to +49
}
result
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

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

main() prints a formatted top-level error via format_top_level_error(e) but then returns the same Err. When main returns Result, Rust’s termination handler will also print the error (Debug), so users may see duplicate error output. Consider handling the exit yourself after printing (e.g., print + std::process::exit(1)), or return a type that doesn’t auto-print on Err.

Suggested change
}
result
std::process::exit(1);
}
Ok(())

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Valid — pre-existing behavior. The format_top_level_error was added for user-friendly hints (e.g. 'check your .env file'), and Rust's Debug output adds noise. Will switch to print + process::exit(1) in a follow-up.

self.settings.llm_backend = Some(b.trim().to_string());
} else if env_or_override("NEARAI_API_KEY").is_some() {
if let Ok(b) = std::env::var("LLM_BACKEND") {
self.settings.llm_backend = Some(b);
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

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

In quick setup, LLM_BACKEND is accepted as-is via std::env::var and stored even if it’s an empty string. That can lock llm_backend to Some("") and prevent the later key-based auto-detection branches from running. Filter out empty/whitespace values (trim + non-empty) before setting self.settings.llm_backend.

Suggested change
self.settings.llm_backend = Some(b);
let trimmed = b.trim();
if !trimmed.is_empty() {
self.settings.llm_backend = Some(trimmed.to_string());
} else if std::env::var("NEARAI_API_KEY").is_ok() {
self.settings.llm_backend = Some("nearai".to_string());
} else if std::env::var("ANTHROPIC_API_KEY").is_ok()
|| std::env::var("ANTHROPIC_OAUTH_TOKEN").is_ok()
{
self.settings.llm_backend = Some("anthropic".to_string());
} else if std::env::var("OPENAI_API_KEY").is_ok() {
self.settings.llm_backend = Some("openai".to_string());
} else if std::env::var("OPENROUTER_API_KEY").is_ok() {
self.settings.llm_backend = Some("openrouter".to_string());
}

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Valid edge case — pre-existing code. An empty LLM_BACKEND env var would bypass auto-detection. Will add trim + non-empty filter in a follow-up.

# Conflicts:
#	src/cli/mod.rs
#	src/setup/wizard.rs
@ilblackdragon ilblackdragon merged commit a09c023 into staging Mar 22, 2026
14 checks passed
@ilblackdragon ilblackdragon deleted the ux-overhaul branch March 22, 2026 06:50
bkutasi pushed a commit to bkutasi/ironclaw that referenced this pull request Mar 28, 2026
nearai#1277)

* feat(ux): complete UX overhaul — design system, boot screen, onboarding, web polish

Shared design system: CSS custom properties for spacing, typography,
transitions, and color tokens used across web UI and boot screen.

Boot screen: compact feature-tags line showing enabled subsystems
(db, tools, routines, heartbeat, skills, sandbox, embeddings) at a
glance. Downgrade startup info logs (libSQL, webhook, workspace seed)
to debug level since the boot screen now covers this.

Onboarding wizard: model picker with live API fetch, provider-aware
auth flow, improved error recovery and progress display.

Web UI: ARIA attributes, welcome card, streaming debounce,
connection status banner, skeleton loaders, send cooldown.

CLI: doctor command enhancements, status command cleanup,
REPL banner consolidation, shared fmt module.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(ux): Apple-level design refinements — spring physics, glass morphism, chat polish

Merge staging theme support (dark/light/system toggle) and layer UX
polish on top: spring-physics motion, glass morphism depth, chat
experience improvements, and responsive mobile refinements.

Design system:
- Restore and extend design token system (spacing, typography, timing,
  easing) with legacy aliases for theme compatibility
- Add shadow tiers, accent glow, glass morphism, spring easing tokens
- Tokens defined in both dark (:root) and light ([data-theme="light"])

Micro-interactions (Phase 2):
- Spring-overshoot message entry animation (slideUp)
- Spring-scale button press on all interactive buttons
- Tab crossfade animation, tool card smooth accordion (max-height)
- Modal scale(0.95) + blur(8px) entry, toast spring slide
- Sidebar width crossfade, card hover lift

Visual depth (Phase 3):
- Tab bar glass morphism + surface highlight + sliding indicator
- Active tab accent background pill
- Assistant message accent left border, user message bubble tail
- Floating input area (rounded + shadow + margin)

Chat polish (Phase 4):
- Smooth streaming cursor (cursorPulse), message hover timestamps
- Time separators (Today/Yesterday/date)
- Textarea smooth auto-expand, send button glow

Settings & forms (Phase 5):
- iOS-style toggle switches for boolean settings
- Input focus glow, save feedback spring animation
- Welcome card with gradient background + proper spacing
- Sticky settings group headers with glass backdrop

Accessibility & mobile (Phase 6):
- Animated focus ring, prefers-reduced-motion global kill-switch
- Touch target audit (44px min), mobile bottom-sheet modals
- Mobile bottom tab bar, toast redesign (icon + border + countdown)
- Thread hover translateX, badge in_progress pulse

Bug fixes:
- Gateway/TEE popover z-index (tab-bar z-index: 200, popovers 500)
- Connection lost banner as fixed top bar instead of flex child
- Sidebar collapse keeps toggle + new thread buttons visible
- Downgrade noisy startup logs (db, webhook, vector) to debug
- Remove green dot pulse animation on connected status
- Deduplicate confirm-modal in HTML, add tab-indicator div

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(web): mobile layout improvements — sidebar toggle, settings drill-down, tab bar polish

- Fix mobile sidebar toggle: use expanded-mobile class instead of collapsed,
  add backdrop overlay, auto-close on thread select, outside-click dismiss
- Settings: replace cramped horizontal tabs with drill-down navigation
  (category list → detail view → back button)
- Bottom tab bar: add glass morphism, hide theme toggle, flip tab indicator
  to top edge
- Keep thread toggle button visible in collapsed 36px sidebar strip

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(repl): interactive approval selector and transient status lines

- Replace ASCII-art approval box with clean horizontal rule card
- Add inquire-based interactive selector for tool approvals (↑↓ + Enter)
- Selector runs directly from send_status via spawn_blocking, with
  stdin_locked flag to prevent readline from competing for stdin
- Transient thinking/tool-started lines: each replaces the previous,
  all erased before final output (no clutter left in scrollback)
- Esc in selector sends denial so agent never gets stuck

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: widen TurnCost token fields to u64 and remove unused variable

- Change input_tokens/output_tokens from u32 to u64 in StatusUpdate::TurnCost,
  SseEvent::TurnCost, and the thread_ops emit site to avoid truncation on
  large conversations
- Remove unused _routine_engine_for_loop binding in agent_loop.rs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: reduce startup log noise — demote info to debug

Demote routine startup messages (builder, WASM tools, tunnel, WASM
channels) from info to debug so the default log output stays clean.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(web): allow CDN scripts in CSP connect-src directive

Add cdn.jsdelivr.net and cdnjs.cloudflare.com to connect-src so the
browser can fetch marked.js and DOMPurify without CSP violations.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* style: fix cargo fmt in repl.rs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(web): gate turn_cost SSE handler on current thread

Prevents cost badge from attaching to the wrong message when
switching threads or receiving events from background threads.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* ci: retrigger CI

* fix: add missing extension_manager to webhook EngineContext

The webhook trigger path added in nearai#736 was missing the
extension_manager field introduced by nearai#1453.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: ignore RUSTSEC-2026-0049 rustls-webpki CRL advisory

Low impact — requires compromised CA to exploit. Tracked for
upstream rustls-webpki upgrade.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(routines): use fields.join for cron normalization

Use split_whitespace fields instead of re-trimming the original string
to avoid preserving extra internal whitespace in cron expressions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(repl): Apple-style approval card — clean vertical flow

- Drop verbose tool description (the command IS the decision surface)
- Unified vertical pipe layout: ◆ header → │ params → │ selector
- Selector options show keyboard shortcuts inline: Approve (y)
- Compact help message, answered state uses └ to close the flow
- No horizontal rules, no blank-line padding — just breathing room

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor(repl): replace inquire with crossterm for approval selector

Drop the inquire dependency (which pulled in crossterm 0.25, duplicating
the existing 0.28). The 3-option approval selector is now built directly
with crossterm raw mode — same UX, zero new dependencies.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore(deps): upgrade crossterm 0.28 → 0.29, eliminate duplication

termimad (via crokey) uses crossterm 0.29. Upgrading our direct
dependency from 0.28 to 0.29 collapses to a single crossterm version
in the dependency tree. Also migrated termimad::crossterm:: references
to the direct crossterm import.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address review comments — box_top off-by-one, smart_truncate overflow, mobile theme toggle

- Fix box_top() fill calculation: was off-by-one, producing boxes 1 char
  too wide (fmt.rs)
- Fix smart_truncate(): account for "..." in the budget so output never
  exceeds max_chars (repl.rs)
- Move theme toggle to settings sidebar on mobile instead of display:none,
  so mobile users can still switch themes (style.css, index.html, app.js)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* style: cargo fmt repl.rs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address review — retry duplication, CSP connect-src, deny color

- Remove failed message before retry to prevent duplicate user messages
- Revert connect-src to 'self' — CDN hosts only need script-src
- Use red for Deny confirmation in REPL approval selector

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

contributor: core 20+ merged PRs risk: high Safety, secrets, auth, or critical infrastructure scope: agent Agent core (agent loop, router, scheduler) scope: channel/cli TUI / CLI channel scope: channel/wasm WASM channel runtime scope: channel/web Web gateway channel scope: channel Channel infrastructure scope: ci CI/CD workflows scope: config Configuration scope: db/libsql libSQL / Turso backend scope: db Database trait / abstraction scope: dependencies Dependency updates scope: docs Documentation scope: extensions Extension management scope: llm LLM integration scope: orchestrator Container orchestrator scope: setup Onboarding / setup scope: tool/builtin Built-in tools scope: tool/wasm WASM tool sandbox scope: tool Tool infrastructure scope: worker Container worker scope: workspace Persistent memory / workspace size: XL 500+ changed lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants