The Source control empty state named the base by its fully qualified ref ('…no changes ahead of refs/remotes/origin/master'). Show it the way people know it ('origin/master') with a small shortRefLabel helper.
* fix(cursor): read the default marker in cursor-agent model rows
* feat(agent-launch): list a terminal-only agent's own models in the launch picker
* fix(agent-launch): cache Cursor listings and keep the seed for fallback lists
* fix(agent-launch): keep the host catalog for agents that have structured chats
* refactor(agent-launch): resolve the picker's discovery runtime once per render
The launcher's Pi panel now shows Pi's thinking levels on the terminal
surface, narrowed to what the picked model supports, and a pick starts
Pi with `--thinking <level>`. Untouched, Pi keeps its own default.
Co-authored-by: lucas-wu-wakakak <8309814+lucas-wu-wakakak@users.noreply.github.com>
* Show Source Control menu rows on one line with a short status
* Keep Push and Force Push clickable; force push shows remote-only commits it replaces
* Match Force Push description to remote-only commits; list review blockers exhaustively
* Show short statuses for every blocked review action
* Source Control lists Codex models from app-server model/list instead of debug models
The Codex text-action picker read 'codex debug models', a dump of Codex's whole
catalog with prompt templates (40-90 KB per model). Once it passed the 4 MiB
output cap discovery failed and the picker kept Orca's built-in list, and the
dump's hidden entries were selectable. List models with app-server model/list
(hidden rows excluded), over Source Control's own launch, lock and teardown,
with the same 4 MiB input budget and 60 s deadline.
* Bound Codex model discovery input in bytes, key its cache case-insensitively on Windows, and stop the app-server only when it ignores stdin EOF
- Count raw bytes from both streams against the 4 MiB budget.
- Read CODEX_HOME (and other account dirs) case-insensitively on Windows for the host cache key.
- Skip the post-listing kill when the app-server already exited; fail waiting listing requests once discovery settles.
- Report an incomplete model list as such.
- Tests: fake-timer grace and successor admission after a successful listing, method-not-found and unreadable-page fallbacks.
* Key the Codex discovery cache on the env spelling Windows spawn uses
Windows env names ignore case and Node's spawn takes the lexicographically first spelling, so
CODEX_HOME wins over codex_home; the cache key now picks the same one.
* Narrow Codex app-server records with the provider record guard instead of type assertions
* Read Source Control action model picks under the repo's host key, as the picker saves them
Local repos resolved generation under the 'unknown' host key because their connection id is undefined, so the saved pick was skipped and the agent default ran.
* Key Source Control model picks by connection until the repo record loads, and test with a real local repo
* Key a native Codex listing's cache on process.env when no agent env was resolved
Adds commit actions to the Git Graph: check out a commit or branch, cherry-pick, revert, and create a branch from a commit. Conflicts hand off to Source Control with continue and abort. Works on local, WSL and remote hosts.
Shows CI result lines in the chat that opened or pushed to a pull request. They're hidden in the terminal overlay and on the phone until the phone app gets the setting.
* Add Git Graph view showing all branches and commit history
Adds a full-pane Git Graph tab that visualizes the whole repository —
all local branches, remote-tracking branches, tags, merges, and HEAD —
instead of only the current branch's linear history shown in the Source
Control sidebar today.
- Add an opt-in allRefs scope to the shared git-history loader so the
graph query enumerates --branches --remotes --tags HEAD; the default
current-branch path is unchanged. The flag is forwarded through the
local IPC, SSH provider, and runtime RPC boundaries so it works in all
environments.
- New git-graph tab content type with a full-pane GitGraphPane that
reuses the existing graph SVG/row components, virtualizes rows, and
opens a commit's combined diff on click. Wired into the tab strip as a
chip (activate/close/keyboard-cycle) mirroring the simulator pattern.
- Entry point: an Open Git Graph button in the Source Control commits
header (Cmd/Ctrl-click opens in a split).
Closes#4521
Co-authored-by: Orca <help@stably.ai>
* Address review: virtual-tab guards, nav-order, truncation copy
- Treat the Git Graph (and mobile emulator) tab chip as a virtual tab:
add an isVirtual contract to EditorFileTab/EditorFileTabContextMenu so
rename, copy-path, copy-relative-path, and reveal-in-finder are disabled
for tabs whose filePath is a display label, not an on-disk path. Prevents
a rename on the synthetic 'Git Graph' path running a real file operation.
- Forward gitGraphIds through the legacy reconcileTabOrder fallback in
getActiveTabNavOrder so keyboard tab navigation does not drop git-graph
tabs when no split-group layout exists yet.
- Make the truncation banner explicit: 'Showing first N commits across all
branches' (was 'of many'), across all locales.
- Tests: virtual-tab hides path actions; git-graph survives the legacy
nav-order fallback.
Co-authored-by: Orca <help@stably.ai>
* Forward allRefs over SSH relay and web runtime paths
Two boundaries re-destructured git.history params and dropped the new
allRefs flag, leaving the repo-wide Git Graph inert (linear default-branch
history) for SSH and web-runtime worktrees:
- src/relay/git-handler.ts: the relay host's git.history handler forwarded
only { limit, baseRef }. Add allRefs so the SSH host enumerates all refs.
Adds an end-to-end relay test (a feature-branch commit absent from linear
history appears only when allRefs is forwarded).
- src/renderer/src/web/web-preload-api.ts: the web preload history shim
destructured only { worktreePath, limit, baseRef }. Forward allRefs.
Also fix a request-id race in useGitGraphHistory: bump requestIdRef before
the null-context early return so an in-flight fetch from a prior worktree
can't resolve and overwrite the 'idle' state after the worktree clears.
Co-authored-by: Orca <help@stably.ai>
* Support filtered and pinned Git history
Resolve branch and tag roots on the host, keep paging tied to the chosen revision set, and echo the applied filter across Git transports.
Co-authored-by: Matheus Breguêz <matbrgz@gmail.com>
* Add the Git Graph tab and commit details
Open the all-branch view from Source Control or the command palette, preserve older clients' layouts, and keep the sidebar HEAD-only.
* Restore files the main merge left at stale versions
The earlier merge of origin/main kept older or missing copies of 61 files
unrelated to the Git Graph (CI script tests, reliability gates, shard
timings and five terminal runtime files). Take main's version of each so
the PR changes only its own files.
* Limit pinned history to explicit graph scopes
Keep All and HEAD snapshots free of redundant ref names, and filter ref searches before nested-tag peeling. Default history requests retain unpinned paging without evicting graph tokens.
Co-authored-by: Matheus Breguêz <matbrgz@gmail.com>
* Preserve canonical placement for older graph clients
Pass graph-free record deltas through without rebuilding layouts, retain existing empty groups, and translate content creation into canonical groups while preserving hidden graph tabs.
* Retain Git Graph browsing state for the tab lifetime
Own history retrieval, selection and scroll in one renderer controller that retires on close or owner/filter changes. Accumulate parent pages before publishing, share action-time workspace navigation, serialize ref searches, and scope refreshes to the workspace.
* Apply Git Graph branch selections on picker close
Keep checkbox changes local until dismissal, then normalize and apply one chosen ref set. Allow picker reads to use a qualified host scope.
* Keep Git Graph navigation bound to its tab owner
Retain the tab's qualified owner and Git connection across workspace focus changes. Newer selections and refreshes supersede pending parent navigation without losing fetched pages.
Report stale workspace switches and let the commit menu handle empty space in the Branch / Tag cell. Cover twin-owner retention, superseded navigation and picker apply-on-close with renderer regressions.
* Translate desktop layout commands against canonical state
Resolve older desktops' visible groups, tab gaps and split paths inside the canonical mutation, including detach and cross-partition moves. Keep initial admission visibility-aware and cache graph presence for binding-only frames.
* Refine Git Graph picker and owner transitions
Discard branch drafts on Escape or owner changes, follow updated tab ownership, and report workspace lookup failures for the current owner. Keep parent navigation queued behind pending history loads.
* Drop the redundant default from the exhaustive layout command switch
* Align Git Graph with changed-code quality rules
Use typed fixtures and an event guard, keep primitive styling on content wrappers, and prune loaded-commit bookkeeping after render.
* Route Git Graph opening through the existing editor action
Use workspace record selectors for stable owner inputs and remove two
loading labels orphaned by the graph changes from all locale catalogs.
* Preserve Git Graph selection and keep row content visible
Refresh and token recovery retain loaded depth and selection in one
accumulating operation. Keep the published view until it settles, and
only fall back to the first commit after history is exhausted.
Prioritize current and open-workspace refs, reserve their markers, clip
wide graphs and retain filenames when long paths need truncation.
* Bound Git Graph refresh and allow graph column resizing
Restore the loaded depth without searching for vanished selections, retain the published result on retrieval errors, and keep newer selections visible. Allow the bounded graph column to expand to every lane through the existing resize mechanism.
* Retain Git Graph history for errors without a message
Detect failed retrieval independently of its message and cover an empty-message rejection in the existing partial-failure regression.
* Retain Git Graph widths and preserve shifted selections
Separate committed and live resize widths, retaining the chosen width for the graph tab lifetime. Allow one bounded extra history page for selections shifted by new commits, and give the resize separator a translated label.
* Preserve nullable Git Graph owner fallback
* Fix Git Graph layout imports after the RPC method move
* Route the Git Graph owner connection through the source-control connection helper
* Keep the Git Graph button idle in hidden Source Control sections
* Re-probe Git Graph support when the owner's runtime status changes
---------
Co-authored-by: Orca <help@stably.ai>
Co-authored-by: Matheus Breguêz <matbrgz@gmail.com>
* feat(checks): show when each check ran and how long it took
Check rows in the right-sidebar Checks panel now show how long a
finished check took and how long ago it finished (or started, while it
runs), with exact start and finish times on hover. GitHub check runs,
legacy commit statuses and GitLab jobs carry provider run times; rows
without times show nothing.
A GitLab pipeline that ran on a commit the merge request has since moved
past says so. The GitHub rollup read keys its 60s cache by the expected
head, so a push never serves the previous head's checks.
* test(checks): bound the clock work of check time labels
* perf(checks): rebuild check time text only when its data changes
Clock ticks now move only each row's age; exact times, duration and copy
are built once per data or language change. Move the run-time mapping
tests to the mapping layer to keep the client test file within budget.
* fix(checks): annotate the gh pr checks reply instead of casting it
* fix(checks): say when a running check started; key the rollup cache by a query comment
A running row reads 'started 5m ago' so its age isn't read as when it
ran. The expected head now rides in a GraphQL comment, which servers
ignore but gh's cache key includes. Add the new copy to the English
catalog and test behavior rather than formatted copy.
* fix(checks): let check times truncate before names in a narrow sidebar
At the narrowest sidebar the time text never shrank, squeezing check
names to nothing and pushing status labels out of their rows. The time
now sits beside the name, gives up width first, and the name keeps a
minimum width.
* fix(checks): rebuild check time copy when translation catalogs change
* fix(checks): reserve room for a running check's growing start age
* test(checks): keep REST check-run times covered through the schema
* fix(checks): read the checks rollup without gh's disk cache
gh's cache key ignores the PR head, so after a push it answered with the
previous head's checks, and it never evicts expired entries. Orca's own
head-keyed checks cache and read coalescing already bound the requests.
* fix(checks): hide check times in rows too narrow to show them
Below a 17rem row the time text is hidden, so the narrowest sidebar keeps
main's name, status and link layout; wider rows truncate the time before
the name, which keeps a 36px minimum.
* fix(checks): truncate a check's duration before its age
The age answers when a check ran, so in a tight row the duration now
gives up its width first.
* test(checks): type the rollup fixture
* fix(checks): show check times only in rows with room for them
Between a 17rem and 19rem row the times squeezed names to their minimum
and clipped; hold them back until 19rem, which the default 350px sidebar
clears.
* fix(checks): keep a minimum name width only while check times show
Rows too narrow for times, including the narrower folder-workspace parent
PR rows, now lay out exactly as before.
* fix(checks): show a running check's elapsed time instead of its start age
'started 10m ago' reserved so much room that running check names shrank
to a few characters at the default sidebar width; beside a running status
the elapsed time ('10m') says the same thing in a quarter of the space.
* test(checks): use a type import for the mocked module
* feat(gitlab): filter Tasks merge requests to ones you wrote or review
Tasks > GitLab > MRs could only filter by state, so on busy projects you
had to scan every open MR to find yours. A second set of chips (Everyone,
Mine, Needs review) now combines with the state chips, so "my merged MRs"
works too.
The filter runs on the host that owns the repo's GitLab sign-in, looks up
the signed-in user fresh on the project's own host, and filters by
author_id or reviewer_id in one request per project. A failed lookup
shows an error instead of everyone's MRs, and older paired servers that
would drop the filter ask to be updated.
Co-authored-by: Yash Kakade <yashkakade2015@gmail.com>
* fix(gitlab): keep filtered MR lists honest while loading and on old servers
- The reviewer chip reads "Reviewing": GitLab lists MRs you were asked to
review even after you reviewed them, so "Needs review" overpromised.
- Rows belong to the query that loaded them: after a chip change the list
shows loading, never the previous filter's rows with Start enabled.
- A refresh checks each paired server's filter support once, not once per
project; failed checks show the real error instead of "update the server",
and the web client sends the filtered list to the runtime it checked.
- Signed-out GitLab gets a sign-in message, and an unresolved SSH project
under a filter shows an error instead of an empty list.
Co-authored-by: Yash Kakade <yashkakade2015@gmail.com>
* fix(gitlab): pin filtered MR lists to the server that confirmed support
A refresh now shares one support check per paired server and pairing, and
the filtered list is sent to the exact runtime and pairing that answered,
so a server replaced mid-refresh can't serve "Mine" unchecked. The web
client scopes its shared check to the current pairing and reuses the
existing result unwrapping. The capability test now counts real status
probes: one per server per refresh for 12 or 120 projects.
Co-authored-by: Yash Kakade <yashkakade2015@gmail.com>
* fix(gitlab): discard filter-support answers from a replaced pairing
A paired server re-paired while Orca was asking whether it supports the
MR filter no longer leaves "update the server" from the old pairing's
answer; the check fails with the usual pairing-changed error instead.
Signed-out detection no longer matches a 401 inside a host or port.
Co-authored-by: Yash Kakade <yashkakade2015@gmail.com>
* test(gitlab): type the filter-change test's props without a cast
---------
Co-authored-by: Yash Kakade <yashkakade2015@gmail.com>
* fix(native-chat): keep a Claude chat's effort changeable after its first turn
After its first turn a Claude chat saves the model id Claude reports it ran
(claude-opus-5-5), while the model list names that model only by its alias
(opus). Every capability lookup matched on the row id, so the chat's model
had no effort choices and effort turned into display-only text on desktop
and phone.
A listed row now describes a model id by its own id or by the model it
resolves to. The match supplies capabilities only; the chat keeps showing and
sending the exact model it ran, so it never drifts when the alias moves. The
host applies the same rule to the row it adds for the chat's model, for
clients that read the answer's models.
* test(native-chat): cover exact-row precedence for a described model id
* fix(native-chat): remember an after-reply effort without changing the new-chat model
An effort pick in a chat on the resolved id it ran is remembered under the
listed row that runs that model and leaves the new-chat model untouched, as
the common pattern keeps the sticky model and updates only its options. The
resolved id is never stored as a default.
* refactor(native-chat): move the remembered-pick model choice into its own module
* fix(native-chat): keep a held effort pick from adopting a new-chat model
* test(native-chat): run the held-effort case as a Claude launch
* revert(native-chat): drop the host-side capability copy no live answer can use
Claude's live session listing carries no resolvedModel, so the host never matched there, and phones draw the host-served chat bundle, so every client already runs the shared lookup.
* Key Claude's model rows by the model each one runs
Claude's listing named rows by alias (opus), while a chat saves the model
it ran (claude-opus-5-5) after its first turn, so equivalent chats held two
kinds of model value and a chat's effort could go read-only. Rows are now
keyed by the versioned model they run, the common pattern: a pick saves the
same id the CLI reports. Aliases stay as their own rows with their own
effort levels; the picker lists one only while the chat is on it or a
search matches it, and the phone list leaves them out. A saved alias is
kept as the user picked it. A configured alias default marks the versioned
row it runs, and an effort picked in an existing or resumed chat no longer
changes the model new chats start on, whatever ids the chat and listing use.
* test: give the delayed-report alias test's timeout its reason
* Remember a new chat's picks by its first turn, and keep aliases reachable everywhere
- A chat counts as existing once it has a turn or a send, or when it was
opened rather than launched here, not once its launch published: an empty
new chat's effort pick adopts its model for new chats again, and every
agent's existing chat keeps the remembered new-chat model.
- Only a row that names itself as the model it runs covers an alias, so an
older host's alias rows stay listed beside a chat's stand-in row.
- The phone lists an alias of a listed model behind a More models toggle.
- The terminal-chat and commit-message model lists carry each row's
resolvedModel, so they list aliases the same way; an older remote that
omits it keeps its full list.
* fix(mobile): keep the More models toggle's state without an effect reset
* Keep commit-message aliases selectable and their resolved model out of saved settings
The Source Control model Select has no search, so it lists alias rows after
the versioned ones instead of hiding them. Discovered models are saved
without an alias row's resolvedModel, and the refresh compares that same
saved shape, so the settings cache gains no field.
* Drop an unrelated audit-tools lockfile change; reset the phone's More models toggle when its drawer closes
* test: build the saved-models test's settings without a cast
* fix(agents): paired-host agent tabs use the host's launch settings
Agent tabs opened on a paired host were replacing that host's permissions and Arguments with the desktop's settings. Let the host plan fresh launches, carry the user's choices separately, and preserve existing prepared launches when an older host cannot express them. Action Arguments replace configured Arguments while inheriting the host's permission settings; resumed sessions keep their recorded recipe.
* fix(agents): paired-host agent tabs use the host's launch settings
Preserve per-launch permissions on older paired hosts by compiling picks they cannot apply from their own settings. Keep authored Arguments separate from saved model defaults so a command's pinned model wins. Use the host's reported shell and execution platform for argument quoting, and remove unused prompt-delivery wrappers.
* fix(agents): paired-host agent tabs use the host's launch settings
Replace recognized permission flags from older paired hosts when a launch picks another permission, while preserving unrelated Arguments and ambiguous prepared launches. Keep multiline drafts working on Command Prompt hosts and omit chat-only permissions from terminal requests.
* fix(agents): paired-host agent tabs use the host's launch settings
Preserve per-launch permission choices that suppress the paired host's Full access flag even when the choice emits no flag itself. Compile these choices from the host snapshot when older launch preferences cannot carry them, while keeping unsupported OpenCode terminal permissions filtered out.
* test: merge duplicate imports in paired launch options test
* test: drop type assertions in paired launch options test
* chore: lower focus-read ratchet baseline after removing creation wrappers
* fix(agents): paired-host agent tabs use the host's launch settings
Keep accepted inline fork drafts delivered when their mirrored tab is unavailable, and cancel launches whose pairing changes during negotiation. Keep action Arguments metadata on the tab route so injected workspace launch callbacks retain their contract. Update consumer fixtures to run lazy compatibility preparation and verify the final launch inputs and cancellation boundary.
* fix(agents): paired-host agent tabs use the host's launch settings
Keep pairing changes during legacy launch preparation silent and preserve the prompt for retry. Make picker tests exercise structured model picks on modern hosts while retaining prepared-command coverage for older hosts.
* fix(agents): paired-host agent tabs use the host's launch settings
A permission pick on an older paired host could turn desktop-configured Arguments into a launch override, including the desktop's model. Keep settings-seeded text out of the paired picker request so the host snapshot supplies defaults, while preserving explicitly edited launch Arguments.
* fix(agents): paired-host agent tabs use the host's launch settings
Keep permission-control choices separate from Arguments the user edited.
Fresh paired terminal launches now share that ownership projection in the
new-tab picker and continuation dialog, so a control change preserves the
host's executable and environment settings. Explicit Arguments edits,
including clearing the field, keep their existing launch behavior.
Cover old-host and modern requests, explicit terminal/chat edits, and the
continuation Start handler. Workspace creation keeps its existing path.
* fix(agents): paired-host agent tabs use the host's launch settings
Older paired hosts could inherit the desktop's model and permission defaults when the launch picker was configured or reopened. Read picker defaults and prepared launch recipes from the executing host, and compile edited Arguments using its settings. Preserve explicit empty edits, narrower permissions and draft prompts.
Keep inherited choices neutral while host settings are unavailable, retaining the existing compatibility launch when no usable host recipe exists.
* chore: merge duplicate type import and drop dead test branch
* fix(agents): paired-host agent tabs use the host's launch settings
Actions with explicit permission choices could unnecessarily read host settings and fall back to desktop preparation when those reads failed. Keep representable action Arguments on the host's authoritative launch path, and read inherited permissions only when the action needs them.
Cover an explicit empty Arguments override and Ask with unavailable settings, preserving the host executable and environment without a permission bypass.
* fix(agents): paired-host agent tabs use the host's launch settings
Supply the paired host's default agent in the shortcut test instead of
assuming the desktop's default applies. Keep a conflicting desktop default
so the shortcut continues to verify the paired host owns its selection.