Sync upstream master (v0.31.2) — 118 commits with extracted module preservation#453
Merged
Conversation
…ider::set_model A session saved against a named OpenAI-compatible profile persists its model as <profile>:<model> (e.g. tokenrouter:MiniMax-M3). On resume the standalone OpenRouterProvider stored this verbatim and leaked the prefix to the upstream API, which rejected it as an invalid model id. The same gap broke subagent model selection for custom providers. OpenRouterProvider::set_model now strips the leading <profile>: routing token whenever the prefix is not a built-in routing prefix and matches either the provider's own profile id or a known built-in OpenAI-compatible profile. Built-in prefixes (claude:, openai-api:, copilot:, ...) still round-trip verbatim so cross-provider session switches keep working. Fixes #382 (defect 1), #383, #363.
…limits Resuming a session with >20 large images failed with Anthropic 400 'image dimensions exceed max allowed size for many-image requests: 2000 pixels'. jcode only enforced a 20MB byte cap and never clamped pixel dimensions, so every stored screenshot replayed into one request crossed the >20-image threshold and tripped the 2000px per-edge limit. Add image_clamp at the MultiProvider::complete_with_failover chokepoint: count images in the request, pick the per-image edge limit (2000px for >20 images, 8000px otherwise), and downscale any oversized image (aspect-ratio preserving) before dispatch. Uses a cheap header-only dimension probe to avoid decoding/cloning when nothing needs clamping. Fixes #381.
Providers persist an assistant turn as [Text, ReasoningTrace, ToolUse], so the resume/re-render path appended reasoning markup into the message text in stored block order, displaying the thinking *after* the answer. Live streaming shows reasoning before the answer, so resumed sessions looked wrong. Accumulate reasoning separately and prepend it to the answer text at each flush point to match live ordering. Adds a regression test using the real stored block order.
Add a shared DP-based fuzzy matcher (crate::tui::fuzzy) for slash-command suggestions. It tolerates substitutions, adjacent transpositions, and extra typed characters (budget scales with query length), uses fzf-style boundary/ consecutive/prefix bonuses, and anchors on the first command character. The final typed character must genuinely match so a completed token does not fuzzily expand into an unrelated command. Whitespace stays significant. Use it for rank_suggestions, and highlight (underline+bold) the matched characters in the command-suggestion list, matching the model/account picker behavior.
…st run When onboarding parks in the Login/LoginOpenAi phase, the welcome-screen key handler ran before the pending-login submit path. After the user picked a provider (e.g. OpenRouter) and typed their API key, pressing Enter was intercepted and re-opened the provider picker, so the choose-provider -> enter-key cycle repeated forever (and y/n/h/l/j/k were eaten as nav keys). Skip onboarding key interception while a pending_login or pending_account_input text entry is active. Adds a regression test.
Add OpenClaw (~/.openclaw/agent/auth.json) and Hermes
(~/.hermes/auth.json) as external credential import sources alongside
OpenCode and pi.
- OpenClaw is a pi fork and shares the flat { provider -> oauth|api_key }
schema, so it reuses the pi-style extractors.
- Hermes uses a nested credential_pool / providers store with
access_token/refresh_token and expires_at_ms (or RFC3339 expires_at);
add source-aware extraction that flattens it to the shared map.
- Map OPENAI_API_KEY to Hermes' openai-api provider id.
- All imports remain consent-gated via unconsented_sources /
trust_external_auth_source and flow through onboarding automatically.
Adds tests for OpenClaw api_key/oauth and Hermes api_key/oauth
(epoch-ms and RFC3339 expiry). Documents the import sources in
docs/AUTH_CREDENTIAL_SOURCES.md. Bumps version to 0.30.0.
Adds an end-to-end regression for the OpenRouter login loop that types a fake API key character-by-character through the real production key dispatch (handle_key) and presses Enter, asserting the provider picker never re-opens and the pending login is consumed. Verified it fails when the pending-login guard in handle_onboarding_continue_prompt_key is removed, complementing the existing helper-level regression test.
…st loop-free Extends the end-to-end key-entry test to verify the typed API key lands on disk at $JCODE_HOME/config/jcode/openrouter.env as OPENROUTER_API_KEY and is exported to the process env. This proves Enter genuinely saves a usable credential rather than merely escaping the picker loop.
…th a /login hint Selecting "No" on the first-run "Log in to OpenAI?" prompt previously opened the inline provider picker and parked onboarding in a login phase, dropping the user into the per-provider pending-login input. That input widget is flaky (typed characters were not echoed), so users could get stuck. Now "No" finishes onboarding immediately and lands the user on the usual new-session screen with a system message telling them to run /login when ready. The provider picker and API-key flow are still reachable via /login. Bump version to 0.30.1.
Custom OpenAI-compatible providers rely on per-model context_window config when no /v1/models endpoint is available. That value was honored only by the provider instance's own context_window() method; every other resolution path (TUI info widget, compaction budget, model switching) went through the global CONTEXT_LIMIT_CACHE and fell back to the 200k default. Seed the cache from named provider model configs on initial config load and on every config reload so the configured limit is respected globally.
… limits Many bundled OpenAI-compatible gateways (Z.AI/GLM, Moonshot/Kimi, MiniMax, Qwen, MiMo, DeepSeek, etc.) serve /v1/models entries without a context_length field, so their models silently fell back to the generic 200K default even when their real window differs (e.g. GLM-5.2's 1M, GLM-4.5's 128K). Add a single profile-agnostic open_weight_family_context_limit() classifier in the central resolver, checked after the live catalog and dynamic cache so a live /v1/models response or an explicit user context_window config always wins. Wire the OpenAI-compatible profile static-limit table to delegate to it so the same source feeds both the per-provider static_context_limits and the global cache. Add an exhaustiveness test asserting every model shipped in a profile's static catalog resolves to a known context window, plus per-family value assertions. Bump to 0.30.2.
Inside multiplexers like herdr, tmux, zellij, and screen the outer terminal's identity env vars are masked or rewritten, so env-based protocol detection returns None and the picker silently degrades to blurry halfblocks. herdr in particular forces TERM=xterm-256color and sets HERDR_ENV=1 in every pane while passing kitty graphics through to a capable outer terminal. Switch the default policy to probe-on-env-miss: trust env detection when it already identifies a graphics-capable terminal (fast, no probing), otherwise run the authoritative stdio capability probe instead of defaulting to halfblocks. The probe is bounded by ratatui-image's 2s timeout, runs after alt-screen entry, and falls back to the font-size-correct fast picker on failure. Adopt only the probe's protocol decision so HiDPI cell sizing stays correct. Add explicit multiplexer detection (herdr/tmux/zellij/screen) for logging and future per-mux behavior. JCODE_MERMAID_PICKER_PROBE=1/0 still forces probe on/off. Adds unit tests for protocol inference, multiplexer detection, and init-mode decisions.
…s reachable The LLM precision judge (consensus rerank) is the only memory mode that is reliably productive (injection precision ~1.0). Previously memory defaulted to the no-LLM hybrid path (memory_sidecar_enabled=false), which is low-precision. - Default memory_sidecar_enabled=true so the LLM judge is on by default; the no-LLM hybrid path is now an explicit opt-out. - Add Sidecar::llm_backend_available() and memory_runtime_active(): when sidecar mode is on but no LLM backend is reachable (logged out / lost access), memory goes dormant instead of silently degrading to the low-precision path. - Re-evaluate LLM/sidecar availability live (MemoryAgent::live_sidecar) instead of caching at construction, so gaining/losing a login is reflected without a restart. - Route extraction/transcript paths through memory_llm_judge_available().
Drives the real App state machine + real renderer (no user data) to score onboarding flow efficiency: - Tier 0: coverage/anti-drift (wildcard-free phase classifier, screen+path coverage) - Tier 1: static flow structure (keystrokes, decisions, screens-to-ready) - Tier 3: per-screen quality from real rendered copy (reading load, key-hint consistency, escape hatch) Emits a scorecard + regression guards.
…tself Parameterizes Tier1/Tier3 scoring with tunable weight structs and adds a meta-evaluation layer that validates the scoring SYSTEM (not the flow) on five properties, each a CI-guarding test plus a consolidated meta scorecard: - monotonicity: making a flow/screen worse never raises its score - anchoring: hand-built known-good/known-bad land in expected bands - discrimination: good vs bad anchors separated by >= 40 points - robustness: ranking stable under +/-50% weight perturbation (400-trial sweep) - signal liveness: every signal demonstrably moves the score Meta-Trust 100/100 (5/5).
Add an exhaustive, closed-enum attribution system so every memory surfacing turn is classified by whether the LLM precision judge actually ran, and if not, exactly why it converted to no-LLM mode. - memory_judge_metrics: 8-variant JudgeDecision (judge_ran + 7 no-LLM paths), per-variant atomic counters, snapshot() with conversion_rate and headline degradation_rate, rate-limited WARN on each degradation. - memory_rerank: RerankOutcome + *_attributed variants so the previously silent hybrid fallbacks (transport error / unparseable / all judges failed / trivial single) self-report instead of vanishing. - memory_agent: record() at every terminal surfacing branch (NoBackend dormancy, judged/fallback rerank, cadence carry, explicit opt-out). - memory_log: per-session judge_decision records. - debug: 'memory-judge' server command surfaces the live snapshot.
Implements OpenAiEmbeddingBackend (POST /v1/embeddings, batched, OpenAI-
compatible base URLs incl. OpenRouter). active_backend() now auto-selects
the remote backend when memory_embedding_backend is configured and a key is
available, else falls back to local MiniLM.
Live path: ensure_embedding tags entries with the active model id; query
embeds (find_similar[_scoped], memory_agent context) route through
embed_query_active; hybrid_fuse and score_and_filter gate the dense half by
active model id so mismatched-vector-space memories stay reachable via BM25.
Config: memory_embedding_{backend,model,base_url,dim} + env overrides.
Benchmark (memory_recall_bench, corpus 7fe469b5, 28 judged), local vs OpenAI
text-embedding-3-small via OpenRouter:
dense r@10 0.304 -> 0.470
hybrid r@10 0.679 -> 0.705 (best), r@5 0.530 -> 0.549
…-ownership) Layer A: SignalSpec registry classifying every candidate onboarding signal as Scored/Deferred/Rejected with explicit rationale. Layer B: coverage metrics over acknowledged-relevant signals. Layer C: FeatureClass probe verifies every on-screen dimension is owned by a scored signal, driving the real renderer (anti-drift). signal_coverage_scorecard prints the full scorecard (87.7/100); signal_coverage_scored_signals_are_all_live guards that each Scored signal maps to a live scoring term.
Adds cross-screen and behavioral signals the per-screen Tier 3 rubric
can't see, each measured from the REAL screens or by driving the REAL
app (anti-drift):
- terminology_consistency: one verb ('log in') across all welcome prose
- progress_visibility: 'N of M' shown in multi-step contexts
- default_safety: a timed auto-commit resolves to a recoverable phase,
never a terminal that discards the session (driven via DECISION_TIMEOUT)
- narrow_terminal_safety: core Yes/No options survive a 50-col terminal
All four are registered as Scored in the signal registry (13 scored now)
with liveness + monotonicity meta-proofs. Honestly-Deferred neighbours
(min_vs_actual_path, back_navigation, jargon_density) are registered with
rationale rather than faked as Scored.
Composite onboarding score 85.5; signal-coverage 86.0 (13/20 acknowledged,
100% on-screen feature ownership). All onboarding/meta/coverage tests green.
Implements the section-1 (path efficiency) taxonomy as a flow graph (nodes = phases + virtual Start, edges = authored transitions w/ keystroke cost), kept anti-drift by a wildcard-free phase_to_node match and a new onboarding_eval_graph_fidelity test that drives the real app: - min_vs_actual_path : authored default vs Bellman-Ford shortest to ready - first_input_latency : keystrokes before the first real action - irreducible_decisions: forced choices with no timeout/auto default - dead_end_screens : non-terminal nodes with no forward edge - cycle_freedom : flow graph is a DAG (DFS back-edge check) All five registered as Scored (min_vs_actual_path promoted from Deferred), with liveness + monotonicity meta-proofs. Tier 5 scores 86.2; composite 86.1. ContinuePrompt is intentionally Start-unreachable (retained compat phase), documented in the fidelity test.
Implements the section-2 (cognitive load) taxonomy. A body_prose_lines extractor strips the fixed chrome (telemetry header, ASCII logo, key-hint line) so analysis sees only human sentences, then derives: - reading_grade_level : Flesch-Kincaid via a vowel-group syllable counter - options_per_screen : Hick's law on simultaneous choices - jargon_density : hits/100w against a small explicit lexicon - new_concepts_per_screen: distinct domain concepts named - number_of_questions : interrogatives to resolve - negation_count : confusing don't/not/never phrasing All six Scored (reading_grade_level + jargon_density promoted from Deferred), with liveness + monotonicity meta-proofs. Tier 6 scores 95.6; composite 83.0 (now spanning 6 tiers). All onboarding/meta/coverage tests green.
Implements the section-3 (clarity & guidance) taxonomy:
- single_primary_action: one CTA/question per screen (no competing asks)
- action_verb_clarity : instruction lines lead with an imperative verb
- next_step_visibility : screen describes what happens next
- expectation_setting : multi-step contexts state scope up front
Instruction detection cues only on explicit action markers (press/type/
choose/run/CTA), so framing prose ('First, log in to get started.') is
correctly not treated as an instruction. single_primary_action promoted
from Deferred. All four Scored with liveness + monotonicity meta-proofs.
Tier 7 scores 98.4; composite 87.7. All onboarding/meta/coverage green.
Drive the real App for 5 behavioral signals:
- back_navigation: decline OpenAI -> /login recovery is offered (not a dead end)
- error_recovery_depth: 1 keystroke (Enter) from recovery Login{None} to picker
- repeated_prompt: declining the only import candidate leaves the import phase
- confirmation_for_destructive: wildcard-free phase classifier, none mutate data
- timeout_safety: do-nothing DECISION_TIMEOUT lands on TranscriptPick/Suggestions
Promotes back_navigation + error_recovery_depth from Deferred to Scored.
Adds liveness proofs, meta_tier8 monotonicity, composite reweight (Tier8 0.09).
Offline timing checks from DECISION_TIMEOUT + rendered copy: - countdown_adequacy: timeout (60s) covers each timed screen's read budget (words / READING_WPS, 4 wps); tightest slack +42.2s - forced_wait: every timed phase honors an immediate-commit key (driven on app) - time_on_blocker: worst-case unattended dwell bounded by DECISION_TIMEOUT Promotes time_on_blocker from Deferred to Scored; adds countdown_adequacy + forced_wait as new Scored signals. Liveness + meta_tier9 monotonicity; composite reweight (Tier9 0.06). Tier 9 = 100, composite 91.1.
Probe the REAL provider-login picker overlay (full-app render), a surface the welcome card alone never shows. Adds InputField feature class and a multi-row List detector for the picker's selectable rows. Registers three structural ownership signals as Scored: - interactive_options (owns Yes/No selector class) - command_affordance (owns /login,/model typed-command screens) - input_field_present (owns the picker's type-to-filter input) Splits Layer C's probe surface (all_probe_surface_texts: welcome + picker) from the prose surface (all_welcome_screen_texts) so the picker's list chrome doesn't skew prose rubrics. Feature coverage 100% over all 5 classes.
Three real-buffer accessibility signals: - no_unicode_dependence: load-bearing prose is ASCII-legible (emoji/box-drawing flagged; graceful punctuation like ellipsis/curly-quotes/dashes whitelisted) - color_independence: selection marked by a non-color video attribute (REVERSED), verified by diffing the real buffer between highlight states - screen_reader_order: explanatory prose precedes the action row (linear order) narrow_terminal_safety already lives in Tier 4. Liveness + meta_tier10 monotonicity; composite reweight (Tier10 0.05). Tier 10 = 100, composite 91.6.
Adds the 4 fundamentally user-dependent signals as Rejected, each with a rationale + the offline proxy that stands in for it: - actual_completion_rate (needs real funnel; no user data by design) - time_to_value_real (needs telemetry; Tier 9 bounds flow-imposed timing) - subjective_confusion (needs surveys; proxied by Tier 6 cognitive load) - drop_off_point (needs analytics; proxied by Tier 5 dead_end_screens) Registry: 42 Scored, 6 Rejected, 0 Deferred. All 8 taxonomy sections covered.
Delete transient experiment logs (ens*.log, *_err.log, jf.log) and leftover demo media (jcode_demo_jaguar.avif, jcode_replay_*.mp4, screenshot.png) that were accidentally committed at the repo root. Add root-scoped gitignore rules (/*.log, /*.avif, /*.mp4, /*.png) so these do not recur, while leaving real assets under assets/, docs/, ios/, and tests/ tracked.
… non-judged memory Previously the judge-failure paths (transport error, unparseable response, all-judges-failed) and the single-candidate bypass silently fell back to unvetted hybrid order. The metrics added in 7081492 exposed these as no-LLM degradations; this closes them. - memory_rerank: on ANY judge failure the rerank now returns EMPTY instead of hybrid order; single candidates are JUDGED (no bypass), so the judge is the only thing that can surface memory. Dropped the TrivialSingle outcome. - memory_agent: on a judge failure this turn, carry the last judge-verified set (judge-backed, like cadence carry) rather than surfacing the empty result or hybrid noise; don't advance last_rerank_turn so the next eligible turn retries the judge. Factored the verified-carry into a shared carry_verified() helper. - memory_judge_metrics: dropped TrivialSingle variant (7 variants now). Net policy: the ONLY path that surfaces non-judge-verified memory is the explicit user opt-out (memory_sidecar_enabled = false). Every other no-LLM outcome either rides a prior judge verdict (cadence/failure carry) or goes dormant (no backend).
New [notifications] config section (separate from ambient [safety]): - turn_complete (default true), min secs 120, lower 30s threshold when the session has todos, and only-when-unfocused gating by default. The notification is intentionally compact: title 'jcode · <session> · done in 12m 34s', body '3/5 todos — <first line of final assistant text>'. Sent via Notification Center (osascript) on macOS and notify-send on Linux, fire-and-forget. Wired into both the remote Done handler and local finish_turn, skipping auto-poke followups, queued dispatches, and replay/test harness modes.
- jcode-storage: add session_presence() (per-session id/pid/streaming view) and derive session_counts() from it; repair the active_pids tests that the v0.26 rebase left referencing a nonexistent sleep_assertion field. - jcode-base: StreamingGuard now wraps the storage marker guard and restores the macOS PreventUserIdleSystemSleep power assertion (with pmset test). - jcode-setup-hints: public launch_jcode_in_macos_terminal(extra_args) reusing the Cmd+; hotkey terminal plumbing, plus arg-aware paused shell command. - menubar: dropdown now lists each running session (animal icon + short name, streaming marker), click opens that session via --resume in the preferred terminal; adds New jcode Window (Cmd+N) item; timer runs in common run-loop modes so the menu updates while open.
- Seed 'NSStatusItem Preferred Position' + autosaveName so a freshly created status item lands among the system icons instead of being pushed fully off-screen when another app owns an oversized status item (this machine had a 1672pt-wide OmniWM item that hid every new status item at x=-1302). - launch_jcode_in_macos_terminal no longer uses AppleScript: background helpers can't satisfy the 'control Terminal' TCC prompt, so the osascript path failed with exit 1. open-capable terminals (Ghostty/Alacritty/WezTerm) launch directly; Terminal/iTerm get an executable .command file via open. - macos_launcher: legacy-bundle check now requires an exact-name match; Path::exists() on case-insensitive APFS matched the new Jcode.app and made should_refresh return true forever (fixes pre-existing failing test). Verified live: status item shows count, dropdown lists sessions with icons and streaming markers, clicking a session opens it via --resume in a new terminal window, New jcode Window spawns a fresh session.
- Auto-connect to most recent server when the app launches - jcode:// URL scheme registered; deep-link pairing tested end to end - Keychain store falls back to Application Support JSON when keychain is unavailable (unsigned simulator builds) - ack no longer marks the session as processing (it acknowledges any request, not just messages) - Required CFBundle keys in Info.plist; @preconcurrency AV delegate Validated end to end in the iOS simulator against a real jcode server: pair via deep link -> auto-connect -> send message -> streamed LLM reply.
VS Code integrated terminal and Apple Terminal on macOS 26 corrupt their GPU glyph atlas under heavy per-cell truecolor animation churn (shimmer, rainbow, pulsing tool colors) plus frequent full-frame repaints, causing a fixed set of similar-shaped letters (n/m/r/w...) to render as stale boxes. Same class of bug Anthropic hit in claude-code #60831/#61562. Detect these terminals via a new SystemProfile.fragile_glyph_cache flag and run a glyph-safe TuiPerfPolicy: disable decorative per-cell color animation and cap redraw FPS, keeping the atlas stable while preserving all interactive features. Overridable via JCODE_GLYPH_SAFE_MODE.
When memory is disabled, the widget previously disappeared entirely. Now it still surfaces the stored memory count with a DISABLED badge so the user can see they have memories but recall/extraction are off.
Previously the title collapsed to just the total when nothing was streaming (e.g. '22'). Always render 'streaming/total' (e.g. '0/22', '2/22') so the live streaming count is visible in the menu bar at a glance without opening the dropdown.
The bar count and terminal icon now turn system green while any session is actively streaming, and render in the muted secondary label color when everything is idle. Uses an attributed title (monospaced digits) plus contentTintColor on the button so the icon matches. Adds NSColor / NSAttributedString objc2 features.
Switch the menu bar glyph from the outline 'terminal' to the filled 'terminal.fill' for a bolder, more visible icon at menu bar size.
jcode previously only detected keymap conflicts against macOS symbolic hotkeys and Ghostty. Global-hotkey grabbers like tiling window managers (OmniWM, AeroSpace, skhd/yabai) intercept keys such as Cmd+J / Cmd+K before the terminal sees them, silently shadowing jcode's prompt navigation, with no detection. - Add KeySource::ExternalApp and a per-binding attribution field. - Add keymap/external.rs with pure, unit-tested parsers for OmniWM (settings.toml, Hyper expansion), AeroSpace (mode.*.binding), and skhd (skhdrc), plus readers that locate each config. - Feed external bindings into collect_snapshot and attribute conflicts to the owning app in the /keys report and startup hint. - Enumerate jcode's built-in prompt-jump fallback chords (Cmd+J/K, Cmd+[/], Ctrl+[/]) so conflicts on those keys are actually detected, not just on the configurable scroll_prompt_* fields. Verified against the live machine: /keys now reports Cmd+J/Cmd+K as taken by OmniWM focus.down/up (and Ghostty).
Appending real answer text while a reasoning region was still flagged open left `reasoning_streaming` stale, so the next `open_reasoning_region` early-returned and skipped its blank-line separator. The answer tail then ran straight into the reasoning run (e.g. `...patch + build.Ah, I see`). Enforce the invariant in `append_streaming_text`: real (non-whitespace) answer text always closes any open reasoning region first, so the separator is preserved no matter which path feeds the stream. The duplicate guard in `apply_stream_ops` is now redundant and removed. Adds a regression test for the answer-into-open-region glue case.
…nals (#330) VS Code integrated terminal and Apple Terminal on macOS 26 (Tahoe) corrupt their GPU glyph atlas under jcode's continuous truecolor animations (shimmer, rainbow, pulsing tool colors), rendering similar-shaped letters as identical box glyphs (e.g. "browser" -> "b[]o[]se[]"). The atlas keys on the full 24-bit (glyph,color) pair, so unbounded distinct colors overflow it. Fix: on these terminals, downgrade TrueColor -> Color256 so rgb() emits Color::Indexed. This bounds the atlas keyspace from ~16M to <=256 while KEEPING animations; the only tradeoff is slightly reduced color fidelity. Robust GPU terminals (Ghostty/iTerm2/kitty/WezTerm/Alacritty) are unaffected. Overridable via JCODE_GLYPH_SAFE_MODE=on|off. Also: - One-time startup disclosure (setup-hints) that fires whenever quantization is active, explaining the tradeoff and how to opt out. - Relax the earlier perf 'glyph-safe' policy to keep decorative animations (only the redraw-FPS cap remains as insurance). - Fix a latent u16 underflow in nearest_gray_index (gray values 1..=7), surfaced by the dense color-sweep test, in both color quantizer copies. Verified end-to-end: - jcode-tui/tests/glyph_safe_wire.rs drives a real CrosstermBackend and asserts Indexed colors serialize as 38;5;n and never 38;2;r;g;b. - color.rs sweep test: 32768 colors collapse to <=256 atlas keys when quantized vs >10k distinct keys at truecolor. - Live capture of the shipped binary in forced vscode mode shows zero truecolor SGR sequences emitted.
… and note it alongside the launch hotkey
…ery nudges - New open_resume action, default Cmd+R on macOS, Alt+R elsewhere; wired into local and remote key handlers, help overlay, and /resume command help. - Generic shortcut-hint system (shortcut_hints.rs): when a user runs an action the long way (e.g. typing /resume), surface a throttled one-line tip naming the bound shortcut. Persisted in ~/.jcode/shortcut_hints.json, capped at 3 shows per hint.
…gger their bindings
- Extract a shared lozenge_pill_spans() helper that draws a rounded capsule (half-circle end caps ◖ ◗) with a filled accent fill + BOLD label when selected, or a hollow muted outline when not. Use it for both the Yes/No selector and the Continue pill, so every onboarding button shares one consistent pill style (replacing the old '< ( Yes ) ( No ) >' chevron/paren row). - Trim the 'Log in to OpenAI?' screen: drop the duplicate 'First, log in to get started.', the redundant 'Choose No to skip for now' line, and the 'Enter to confirm.' line. The question + Yes/No pills + the Esc hint (which already says you can /login later) are enough. - Update eval/golden guardrails to the new pill glyphs, and fix the Tier 10 color-independence probe to locate Yes/No labels by cell column instead of byte offset (the lozenge caps are multi-byte).
The hollow (cap-only) unselected pill rendered as stray half-moons that looked broken. Render both pill states as solid capsules: the selected one keeps the bright accent fill + BOLD label, the unselected one uses a muted dark-gray fill with no bold. The BOLD-vs-not contrast still carries the selection on monochrome terminals.
The decorative idle donut was gated on a completely empty transcript, so it vanished the moment any system notice landed (e.g. the 'run /login when you're ready' message left after declining onboarding), leaving a plain screen. Gate on whether a real conversation has started (any user/ assistant/tool/reasoning message) instead, so non-conversational notices keep the idle animation running until the user actually starts chatting.
Search now splits the query into whitespace tokens and requires every token to appear in the session index (order independent) instead of one rigid contiguous substring. 'api deploy' now matches 'deploy ... api'. Highlighting marks every matched token via a per-char mask. Stays O(tokens) per session, fast enough for every keystroke.
- Header client line previously replaced the session-name icon with the http/ws connection icon (🌐/🕸️), so e.g. a remote 'ram' client showed a globe instead of 🐏. Now show the name icon and keep the connection icon as a trailing hint. - Replace session-name emoji that render as tofu or split glyphs on older terminal fonts: ZWJ sequences (polar-bear/raven/crow) and Unicode 13+ additions (cockroach, dodo, fly, mammoth, worm, beetle, goose, moose, donkey, jellyfish, beaver, bison, seal) with single widely-supported codepoints, and drop duplicate-emoji aliases (falcon/hawk/moth). - Add regression tests: session/server icons must be single safe glyphs (no ZWJ, no Unicode 13+) and session names/icons must be unique; header client line shows name icon plus optional connection hint.
…ld model'
The set_route serde alias on Request::SetModel made two variants answer to
the same internally-tagged 'set_route' tag. Serde dispatches by tag (not by
fields), so the SetModel alias shadowed the structured SetRoute variant and
every picker-driven model switch failed with 'missing field model'.
Remove the colliding alias and normalize the legacy {type:set_route,model}
shape inside decode_request instead. Add a regression test for the
structured set_route payload.
Patch release for the set_route protocol regression (#295 follow-up) that broke model switching via the picker with 'Invalid request: missing field `model`'. Shipped in v0.31.1.
Sync quangdang46/jcode with 1jehuang/jcode upstream (v0.25.1..v0.31.2). Extracted module handling: - Keep all 7 adapter/bridge files (casr_adapter, dcg_bridge, dcp_bridge, hashline_edit, import.rs, at_picker, yolo_classifier, rtco_filter) - Keep ForeignSession variant (now alongside PiSession/OpenCodeSession) - Keep all github.com/quangdang46/* git dependencies - Take upstream's Pi/OpenCode as native ResumeTarget variants Conflict resolutions: - .gitignore: keep both our patterns + upstream's stray-artifact ignores - Cargo.toml: keep mobile-core/mobile-sim members, add agentgrep dep - input.rs: keep DCG bridge calls, add new_terminal_key_matches etc - config.rs: add notifications: NotificationsConfig field - session-picker: keep ForeignSession, add upstream Pi/OpenCode support - memory.rs: add memory_llm_judge_available() function - args.rs: add Menubar variant under AmbientCommand Co-Authored-By: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Merge upstream
1jehuang/jcode:master (v0.25.1→v0.31.2) into our fork, preserving all 7 extracted modules and local customizations.Extracted modules preserved
casr_adapter.rs,import.rsdcg_bridge.rs,yolo_classifier.rsdcp_bridge.rshashline_edit.rsat_picker.rsrtco_filter.rsmempalace-adapter/Key conflict resolutions
PiSession/`OpenCodeSession`github.com/quangdang46/*git depsNotificationsConfigfor desktop notification supportmemory_llm_judge_available()functionVerification
cargo check— ✅ 0 errorsForeignSessionreferences preserved across the codebaseUpstream features gained