Skip to content

Upstream sync - #2

Merged
gs-attripathi merged 141 commits into
masterfrom
upstream-sync
May 3, 2026
Merged

gs-attripathi merged 141 commits into
masterfrom
upstream-sync

Conversation

@gs-attripathi

Copy link
Copy Markdown
Owner

No description provided.

1jehuang and others added 29 commits April 30, 2026 05:48
…on this branch

Heavy upstream refactor brought in:
- jcode-protocol crate extraction (replaces monolithic protocol.rs)
- jcode-config-types, jcode-session-types, etc. extracted
- session.rs re-exports types from session/model.rs and submodules
- Rewind / RewindUndo replace the old request shape

Features dropped here (still on master):
- /branches and /checkout RPC dispatch (Request::Checkout removed; no
  compatible server handler in upstream's new routing)
- Backend.rewind / .checkout / .mcp_control helpers removed
- ServerEvent::Rewound / McpResult / McpControl handling stripped
- Session.reasoning_effort field gone in upstream
- ToolPermissionMode kept as a local enum in safety.rs since safety
  code still uses it but the type is absent from upstream's config

Schema kept so tree-branching can be re-ported later:
- StoredMessage.parent_id, Session.active_leaf_id
- StoredDisplayRole::UserCommand
- Session::active_path / leaves / set_active_leaf / find_message_by_short_id
- Session::add_system_note / add_user_command / active_real_leaf
- truncate_messages re-anchors active_leaf_id

Library + binary compile. /branches and /checkout are NOT functional
on this branch — switch to master to use them.
…am-sync

Re-port the features that were dropped during the merge into upstream's
new request-routing layer:

- Re-add Request::Checkout to jcode-protocol with the same shape as
  before. Server-side handle_checkout dispatch lives inline in
  client_lifecycle.rs (no separate handler file in upstream's structure)
  and follows the rewind pattern: lock agent, validate target as a real
  leaf, set_active_leaf + record_user_command + record_system_note,
  then push the new history via handle_get_history.

- Restore Backend::checkout helper that mirrors Backend::rewind: clears
  has_loaded_history so the server's follow-up history payload paints.

- /branches and /checkout slash commands re-added in commands.rs.
  /branches reads from disk so it works in remote and standalone modes;
  /checkout bails out in remote mode so the RPC dispatcher in
  remote/key_handling.rs takes over.

- /rewind and /checkout now also record a UserCommand entry (the typed
  slash command) and a System note (the "✓ ..." reply) on the active
  branch so the user can see in scroll-back what they typed to navigate.

Agent helpers re-added: session_ref, checkout_active_leaf,
record_system_note, record_user_command. Tree primitives on Session
were already preserved through the merge.

Library + binary build clean. Master remains untouched.
…ests cover leaves/active_real_leaf

Three pieces that the previous merge commits had left unfinished:

- All test files now compile. Earlier I only ran cargo check --lib so
  ~22 broken test fixtures slipped through: missing parent_id: None on
  StoredMessage literals (10 in client_session_tests/reload.rs, plus
  one each in 8 other test modules) and missing profile_id +
  static_context_limits on OpenRouterProvider literals (2 sites).
  Mechanical fix.

- Per-tool permission gating restored. Upstream removed
  cfg.safety.tool_permission_mode plus the Registry::ensure_tool_permission
  caller, so safety::tool_permission_requirement was a dead island. Added
  Registry::ensure_tool_permission back at the same point in the
  execute() flow. Mode is now read from JCODE_TOOL_PERMISSION_MODE
  (default Autopilot — same effective behavior as upstream); set it to
  ask|strict|confirm to opt into the gating that master had. Avoids
  having to re-add a config field to the jcode-config-types crate.

- New unit test covers leaves()/active_real_leaf() with a tree that
  has two real branch tips plus navigation notes (UserCommand + System
  appended after a checkout). Confirms /branches won't surface the
  notes as fake branches and the active marker walks back through
  them.

cargo test --lib session::tests::cases passes (27 tests). Library +
binary build clean.
Merge regressed commit 1f78dc8. /info was back to printing
`{short_name} ({id[..8]})` which in remote mode shows the TUI's local
placeholder id (an animal name unrelated to the actual session),
producing things like 'Session: jaguar (session_)' where the parens
just contain the universal 'session_' prefix.

Switch to active_client_session_id() so the displayed id is the real
server-owned session id — the one that exists in ~/.jcode/sessions/.
This is the same fix that was on master before the upstream merge.
Real bug, not the rewind: every time a session was loaded from disk
(load_from_path, load_for_remote_startup, session_from_remote_startup_snapshot)
the new active_leaf_id field stayed None even when messages existed.
On the next append, append_stored_message saw active_leaf_id=None,
gave the new message parent_id=None (treating it as a root), and
advanced the leaf to that new message. From that point on
active_path() walked back from the new leaf, found parent_id=None,
and returned just that one message — so render and provider_messages
both showed only the latest prompt+reply. Prior history vanished from
the UI even though it was still on disk in session.messages.

Add Session::ensure_active_leaf_backfilled() and call it from all
three load paths. Backfill picks the last message's id as the leaf,
preserving linear semantics for sessions that have never branched.

Reproducing test: ensure_active_leaf_backfilled_prevents_history_loss_on_load
constructs a session, clears active_leaf_id (simulating the load
anti-pattern), runs the backfill, appends a new message, and asserts
the active path includes all three messages.
Two changes investigating "user prompts disappearing from chat":

1. Fix: Interleave-path prompts now appear in display_messages.
   When the user presses Enter while is_processing is true (e.g.,
   right after a streaming response finishes but before the Done
   event has flipped the flag back), send_action routes the message
   through SendAction::Interleave -> remote.soft_interrupt(). That
   path was sending the content to the server but never pushing a
   user-styled DisplayMessage to the chat, so the user typed something
   and saw nothing visible (their prompt vanished). Now we mirror the
   prompt to display before sending, matching what Submit already does.

2. Diagnostics: log every push/replace/clear of display_messages and
   the should_apply branch in the History event handler. With these
   on, future "lost prompt" reports can be traced exactly: was the
   prompt pushed? coalesced? replaced? cleared? Useful when the bug
   is intermittent and we need to see what happened in a specific
   session.

The interleave fix solves the most common cause of the symptom. There
is a remaining intermittent render-side issue where a pushed user
message doesn't appear visually even though it's in display_messages
(the diagnostics confirm the data is correct). Reproducing this
reliably needs interactive driving of the TUI; the data layer has been
ruled out so the bug is somewhere in body cache invalidation or the
render-line pipeline.
…s a branch immediately

Three matched changes that together give the user-mental-model rewind:

- /rewind N now keeps the first N user prompts AND each one's full
  assistant turn (replies/tools), instead of upstream's 'keep N
  visible messages' which was off by one for any prompt-reply pair.
  Server dispatch in client_lifecycle.rs calls rewind_to_prompt
  instead of rewind_to_message.

- Re-add Agent::rewind_to_prompt that was removed during the upstream
  merge. Non-destructive: moves the active leaf back, preserves the
  dropped suffix on disk so /branches sees it as a sibling. The tree
  itself replaces upstream's rewind_undo snapshot mechanism.

- Session::leaves() now also includes the current active_real_leaf
  even when it has children. After /rewind the active leaf is a fork
  point with the dropped tail hanging off it; without this the user
  would have to type a new prompt to materialize the rewound state
  as a visible branch in /branches. Now /branches lists both
  immediately and the leaf_label still walks back to the most recent
  user prompt for each, matching 'branch title = last kept prompt.'

Also sticky: the body-cache fix from v0.11.19 (clear_body_cache on
display_messages clear/replace) — that one was the lost-message bug.
This commit is the rewind-semantics layer on top.

cargo test --lib session::tests::cases passes (28). Library + binary
build clean.
Treat any message that is the parent of a navigation note (UserCommand /
System) as a branch tip alongside true leaves and the current active
leaf. Without this, after /rewind + /checkout the rewound fork point
fell out of /branches because it had children (the dropped suffix) and
was no longer the active leaf.

Also remove the unused branch_anchors field on Session — dead
scaffolding that was never initialized or read.
Several fixes that together stop Sonnet from going on a goose chase
when asked to "look something up online":

- Beef up the websearch tool description from "Search the web." to a
  full paragraph spelling out when to use it and what it returns. The
  three-word version was too thin; the model would skip it in favor of
  whatever Claude-Code-shaped tool name (`ToolSearch`, `WebSearch`)
  was in its prior.

- Alias `WebSearch` and `web_search` to `websearch` (and `WebFetch` to
  `webfetch`) since those are direct synonyms in the model's training.
  Do NOT alias `ToolSearch` — Claude Code's `ToolSearch` returns tool
  schemas, not search results, so wiring it to websearch produces
  confusion ("I got results but the model says it has no web search
  tool"). Better to fail Unknown.

- When a tool call fails with Unknown tool, surface a corrective hint
  pointing at the closest jcode equivalent (e.g. `ToolSearch` → "use
  `websearch` instead") plus the full available tool list. Fixes the
  pattern where the model tries one wrong name, sees Unknown, and
  gives up instead of looking at the catalog.

- Tool-list lock now invalidates correctly when MCP tools register
  asynchronously. Previous attempt compared post-filter `locked.len()`
  against pre-filter `tool_count()`, which were perpetually 34 vs 35
  for non-canary sessions and caused an infinite invalidate→re-lock
  loop that burned prompt cache every turn. Track `tool_count` at
  lock time and compare against the live count instead.

- Global instructions (AGENTS.md) now prefer `$JCODE_HOME/AGENTS.md`
  over the shared `~/AGENTS.md` if present. Lets users keep separate
  global files for jcode vs other AGENTS.md-aware tools (e.g. Claude
  Code), so Claude-Code-specific tool names like `mcp__context7` or
  `claude-renames` stop leaking into jcode's prompt.
@gs-attripathi
gs-attripathi merged commit 94b1df7 into master May 3, 2026
1 of 8 checks passed
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