Skip to content

Sync upstream master (v0.31.2) — 118 commits with extracted module preservation#453

Merged
quangdang46 merged 119 commits into
masterfrom
sync-upstream-20260622
Jun 22, 2026
Merged

Sync upstream master (v0.31.2) — 118 commits with extracted module preservation#453
quangdang46 merged 119 commits into
masterfrom
sync-upstream-20260622

Conversation

@quangdang46

Copy link
Copy Markdown
Owner

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

Module Files Lines
CASR (session import/resume) casr_adapter.rs, import.rs 1,758
DCG (destructive command guard) dcg_bridge.rs, yolo_classifier.rs 1,914
DCP (dynamic context pruning) dcp_bridge.rs 197
Hashline hashline_edit.rs 411
FFS (file search, @-mentions) at_picker.rs 600
RTCO (token cost optimizer) rtco_filter.rs 168
Mempalace (memory palace) mempalace-adapter/ 802
Total 5,850+ lines

Key conflict resolutions

  • ForeignSession variant kept alongside upstream's new PiSession/`OpenCodeSession`
  • Cargo.toml — preserved all github.com/quangdang46/* git deps
  • input.rs — kept DCG bridge integration, added new keybinding methods
  • config.rs — added NotificationsConfig for desktop notification support
  • session-picker — kept CASR integration, added upstream's Pi/OpenCode support
  • Memory — added upstream's memory_llm_judge_available() function

Verification

  • cargo check — ✅ 0 errors
  • All adapter files present and intact
  • ForeignSession references preserved across the codebase

Upstream features gained

  • macOS menu bar session/streaming indicator
  • Desktop notifications on long turn completion
  • Configurable keybindings (new terminal, open resume)
  • macOS sleep guard while streaming
  • Onboarding improvements (Pi/OpenCode import)
  • OpenAI TLS transport error retry
  • Token-based AND search in session picker

1jehuang added 30 commits June 17, 2026 07:02
…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).
1jehuang and others added 28 commits June 21, 2026 12:06
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.
…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.
- 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>
@quangdang46 quangdang46 merged commit 71e5683 into master Jun 22, 2026
1 of 10 checks passed
@quangdang46 quangdang46 deleted the sync-upstream-20260622 branch June 28, 2026 13:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants