Commit Graph
5229 Commits
Author SHA1 Message Date
Jinjing b47b2513b8 Show all automation destination hosts, disable ineligible ones (#16665)
* Show all automation destination hosts, disable ineligible ones

Previously, filtering to only eligible hosts hid all connected hosts
on pre-host-scoping Orca servers. Now all offered hosts appear in the
picker; ineligible ones are disabled with a message naming which
servers need updating to support them.

* Show all automation destination hosts, keep create available when all ar

- Gate the create button on what the picker offers, not on readiness: with every
  offered host ineligible (e.g. all pre-host-scoping servers), the dialog is
  where the repair is stated, so the button must still open it.
- Fix StrictMode double-mount lifecycle: disposed controller revived by effect,
  unsubscribe before dispose to prevent event leaks under simulated unmount.
- Validate create destination early, before hooks load and trust prompt, so the
  user never answers a trust dialog for a destination that would reject.
- Dedupe capability probes per authority incarnation: concurrent callers share
  one in-flight status.get; confirmed capabilities never re-probed.
- Drop cache payload at retirement so revived hosts refetch instead of showing
  stale rows.

* Add TTL-based capability probe caching and fix automation dialog target

Extract capability probing to a separate module with improved caching strategy: confirmations now expire after 60 seconds and the cache is bounded to 32 entries, enabling in-place runtime replacements to invalidate old confirmations. For uncaptured automation owners, resolve the dialog target to the same host the save addresses rather than relying on ambient context, preventing stale host references. Optionally await external managers after mutations to ensure row re-reads reflect recent writes.

* Fix automation tests after rebase

* Refactor capability probe to fence fencing checks from in-flight probes

Fencing checks need fresh probes since in-flight probes may predate
in-place runtime replacements. Extract shared probe deduplication
into `sharedCapabilityProbe()` and unconditional probe start into
`startCapabilityProbe()`, then route based on cache preference.
2026-08-26 19:13:27 -04:00
Jinjing af6c15d01b Improve automations table layout and column sizing (#16667)
* Improve automations table layout and column sizing

- Wrap table containers with min-width constraint for horizontal scrolling
- Adjust grid column widths for better visual balance
- Simplify automation draft building with helper function
- Remove unused validation checks and imports

* Make automation list first column sticky

- Keep automation name visible when scrolling horizontally
- Adjust header z-index to layer above sticky cells

* Remove unused canCreateAutomation prop from test
2026-08-26 19:13:27 -04:00
Brennan Benson 28da8901cc fix(automations): validate create destination projects 2026-08-26 19:13:27 -04:00
Brennan Benson 241d9f83fb fix(automations): restore main checks 2026-08-26 18:59:43 -04:00
hwantage b755629f37 feat(i18n): add Korean translations for CLI-created workspace labels (#16212)
- Localize filter toggle labels in SidebarFilter and SidebarWorkspaceFilterSection.
- Localize card detail descriptions in WorktreeCardCliDetailSection.
- Localize meta badge accessibility label in WorktreeCardMetaBadges.
- Resolves English fallback for CLI-created workspace UI under Korean locale.
2026-08-26 11:47:06 -07:00
Jinjing cda2280d63 Show all automations (#16532)
* Add all-host automations with scoped ownership and multi-authority suppo

Enable automations to run on multiple hosts (SSH targets and local) with
owner-fenced mutations, scoped list queries per host, and conflict
resolution. Introduces desktop and runtime authorities as distinct
automation storage owners, with per-host caching, invalidation, and
retry scheduling on the renderer. Captures registration generations for
SSH hosts to survive re-adoption. Adds CLI support for destination
selection and conflict recovery.

* Filter automation create projects by destination host

Only offer projects available on the selected destination, preventing
the mismatches that would fail at submit time. Auto-adjust the project
selection if it becomes unavailable when the destination changes.

* Add runtime storage authority support for automations

- Support both runtime and desktop as automation storage authorities
- Make owner preconditions optional for legacy-client compatibility
- Cache automation list projections to improve performance
- Add per-row repo/worktree resolution for cross-authority collisions
- Extend automation.list RPC to always include owner metadata

* Replace child_process.execFile with runProcess for external automations

- Migrate external-manager to use cross-platform runProcess wrapper per child-process safety policy
- Abstract electron app/ipcMain APIs in orca-runtime via environment accessors
- Install fake app environment in automation tests for consistent setup
- Reorganize imports to use specific module paths (ssh-target-registry, agent-detection, browser-error)
- Remove external-manager from child-process import allowlists (no longer violates direct import)

* Unify desktop automation CRUD onto the local runtime RPC surface

The desktop authority now speaks the same automation.* RPC contract as
remote runtimes, via callRuntimeRpc({kind:'local'}) -> runtime:call ->
the shared RpcDispatcher. The automations:list/listRuns/create/update/
delete/runNow IPC arms, their preload members, and every renderer
desktop-vs-runtime transport fork are retired; the runtime methods are
the single implementation of scoped lists, owner fencing, and change
publication for both transports (mobile clients already exercised them).

The desktop probe scheduler's priority lease survives the move as an
AutomationService hook the IPC registration installs and the runtime
methods take, so Orca's own automation traffic still parks queued
external-manager probes.

External-manager scope arms and dispatch-loop plumbing stay on IPC by
design; automation change events keep their existing channels (renderer
ingestion already converges them by authority).

* Remove automation ghost SSH tombstone scanning

This functionality for synthesizing tombstones for automation-referenced SSH
targets is no longer needed as part of the automation system refactoring.

* Refuse orphan automations at dispatch time, not migration time

Remove migration-time disabling of orphan automations and the `enabledDecidedBy` field. Dispatch now refuses orphans at runtime instead, simplifying state management and UI. Orphans are left unstamped and enabled; dispatch refuses to run them via `resolveAutomationRunTarget`.

* Show all automations in flat table with unified filter menu

- Replace host picker component with comprehensive Filters menu supporting status, last run, agent, and host filters
- Flatten automation list layout to single table instead of host-grouped sections
- Add Host column to display execution host for each automation
- Display active filters as removable pills below toolbar
- Delete unused AutomationHostPicker* components

* Add automation owner fencing and destination validation

- New AUTOMATION_OWNER_FENCING_RUNTIME_CAPABILITY for owner preconditions; legacy clients get owner metadata snapshotted at RPC boundary for compatibility
- Editor captures and revalidates automation destination before save, preventing silent retargeting if SSH infrastructure changes mid-edit
- SSH target types now isolate renderer-authored fields; generation is server-owned and stripped by IPC handlers

* Route automation recovery actions to the origin host

When an automation action fails due to owner fencing, recovery verbs
("Update server", "Reconnect") must run on the host where the refusal
originated: the row's captured owner for row operations, or the
destination the create dialog captured, not the list's filtered host.

* Remove external manager scope limitation notices

Consolidate create destination eligibility checks with a unified predicate
and fix the bug where desktop repo IDs could be sent to runtime hosts where
they cannot resolve.

* Persist only store-derived automation contexts, not client-perspective o

Store contexts must never be based on client-provided runContext or sourceContext
values—clients speak a different perspective (e.g., 'runtime:<id>' for host IDs
they assign), and persisting those makes the store projection orphan automations
it actually owns. Derived contexts now take precedence in create and update paths,
with explicit null still honored to clear a value. Tests verify this by simulating
drift after storage and confirming that moves re-derive while toggles preserve.
2026-08-26 09:50:12 -07:00
Neil 885106baee fix(terminal): stop routing unowned workspaces to the focused runtime (#16584)
`createWebRuntimeSessionTerminalResult` collapsed an explicit
`environmentId: null` ("I resolved ownership and nobody remote owns this")
into "caller said nothing", then fell back to
`settings.activeRuntimeEnvironmentId`. The tab-strip "+" shell rows and the
guest-focus Ctrl+T relay both pass that explicit null, so a local workspace's
new terminal was created against whatever remote runtime happened to be
focused, which answered `selector_not_found` for a worktree id it had never
seen.

The same call selects the runtime as the workspace's execution host before
the create, and the error path never handed that selection back — leaving the
workspace latched to the runtime that just refused it, so every later
owner-routed action (the next Ctrl+T included) silently followed the latch
until a workspace switch reset it.

Fixes #16444
2026-08-26 04:37:17 -07:00
Neil 691759540a fix(terminal): blur suspended panes on the dispose branch too (#16592)
suspendPaneRendering blurred panes only on the WebGL-retention branch; the
dispose branch — taken by every pane past MAX_RETAINED_HIDDEN_WEBGL_CONTEXTS=6
— did not. Make it unconditional so both branches leave a suspended pane in the
same state.

No measured cost is being fixed, and the earlier cursor-blink-timer rationale
was wrong. Measured on Windows 11 against the shipped @xterm/xterm
6.1.0-beta.287 + @xterm/addon-webgl 0.20.0-beta.286, N=12 panes: display:none
and inert each make Chromium fire a real blur on the pane's helper textarea,
which pauses the WebGL blink interval on its own, and disposeWebgl() disposes
the blink manager regardless. Hidden panes measured 0 interval fires and 0 rAF
fires over 8s with and without the explicit blur. Focus is also a document-wide
singleton, so "one timer per hidden pane" was never possible.

Kept as defence in depth for opacity:0 without inert — TerminalOverlaySlot's
startup probe inside an active worktree — the one hide mode that keeps focus.
2026-08-26 03:12:03 -07:00
Neilandsanshengai e2cb797506 perf(sleep): park idle agents in the worktree you are working in (#16591)
The planner skipped the entire activeWorktreeId, so the tree a user actually
works in never parked anything — exactly where a 16 GB Windows host
accumulates its idle Codex/Grok panes and starts hard-paging (#16211).

The two guards that remain are the correct granularity and already existed:
foregroundTerminalTabIds covers the tab on screen, and the
foregroundTerminalLastSeenAtByTabId floor in getEligiblePane holds any tab
left inside the idle window.

Test lever taken from @sanshengai's #16214, which found this first: pinning
the existing sibling-tab regression to activeWorktreeId means it fails against
the pre-fix planner. A standalone background-worktree case does not, because
the fixture's active worktree is a different one — that is why the first cut of
this change shipped a vacuous test.

#16214 changed only the planner suite; the same one-line change also breaks
agent-hibernation-coordinator's two revalidation tests, which used
activeWorktreeId as their eligibility lever. Those now flip
setForegroundTerminalTabIds instead, which is the property they were written
to prove.

Co-authored-by: sanshengai <sanshengai@users.noreply.github.com>
2026-08-26 03:11:29 -07:00
Neil 87f5c6cd03 perf(terminal): stop rebuilding parked-watcher keys on every overlay render (#16596)
* perf(terminal): stop rebuilding parked-watcher keys on every overlay render

Every mounted worktree's TerminalPaneOverlayLayer rebuilt its parked-watcher
synchronization key from scratch on every render: JSON.stringify of the whole
split-tree root per tab, then a second JSON.stringify pass that re-escaped that
already-serialized string. Two app-global subscriptions in the cold-parking
hook (pendingStartupByTabId, sleepingAgentSessionsByPaneKey) made any write for
any tab in any worktree trigger that render everywhere at once, so the cost
scaled with mounted worktree count.

- Memoize the store-derived half of the reconciliation key on the already
  shallow-stable selector output. The captured-pane half still recomputes per
  render because that registry mutates outside React.
- Replace the outer JSON.stringify of already-serialized fragments with a
  length-prefixed join, which is injective for arbitrary fragments and does no
  escaping pass.
- Narrow both global subscriptions to worktree-scoped, value-comparable keys.

Measured on a 12-worktree x 4-tab x 4-leaf-split model: 9.5 us -> 1.3 us of key
work per worktree render (7.3x), before counting the renders the narrowed
subscriptions now avoid entirely.

Key semantics are unchanged: no hash is introduced, only memoization of an
identical serialization and an injective replacement for the outer pass.

* refactor(terminal): narrow the park subscriptions with useShallow, not string keys

Review follow-up. zustand's `shallow` already compares Sets and plain objects
structurally and order-insensitively, so the encode-to-string / parse-back pair
each subscription carried was doing by hand what `useShallow` does for free.

- Restore the Set-returning `selectSleepingRecordParkExemptTabIds` and subscribe
  through `useShallow`. Drops the NUL separator, the `.sort()` that existed only
  to keep insertion order out of the key, the O(k^2) `includes` dedup, the parse
  helper and the caller's `useMemo` — and removes the ordering invariant that
  was enforced by a comment alone.
- Same for the pending-startup presence hook: `useShallow` over the presence
  record, keeping the frozen empty singleton for the zero-allocation steady
  state.
- Drop the `useMemo` around the reconciliation selector. `useShallow` returns a
  fresh closure every render regardless, so the memo bought nothing and its WHY
  comment described behaviour zustand 5 does not have. The memo that is the real
  fix here, `reconciliationStoreInputsKey`, is untouched.

Adds a narrowing case for a sleeping record this worktree can never resume,
which pins both the blocked-record exemption and the narrowing itself; it fails
against the pre-narrowing code (2 renders, expected 0).

Net -25 lines of production code.
2026-08-26 03:08:11 -07:00
Neil 1fafccb26b fix(settings): use Workspace Directory for the Create-project default path (#14767) (#16583)
* fix(settings): use Workspace Directory for the Create-project default path

`repos:getDefaultCreateProjectParent` hardcoded `join(homedir(), 'orca',
'projects')` and never consulted the settings store, so Settings -> General ->
Workspace Directory had no effect on the Location field of "Create new project".
Users had to retype the path every time, or fake it with an NTFS junction.

Resolve the parent from the store instead, through the same rule the rest of the
app uses for a host preference: `host override ?? client default`, i.e.
`getEffectiveHostSetting(settings, LOCAL_EXECUTION_HOST_ID,
'defaultWorktreeLocation', settings.workspaceDir)`. This handler only ever
answers for the local host, and a local-host override previously could not win
either.

A seeded value is not a user choice. `workspaceDir` is never blank -- new
installs seed it with `~/orca/workspaces` -- so treating any non-blank value as
configured would silently relocate every existing user's new projects into the
worktree root. Worktrees nest at `<workspaceDir>/<repoName>/<branch>`, so such a
project would then host its own worktrees inside its own working tree. Compare
against `getDefaultWorkspaceDir(homedir())` (now exported) via
`normalizeRuntimePathForComparison`, and keep `~/orca/projects` for blank,
whitespace-only, and untouched-default values.

Also scope the `~/orca/projects` shorthand in `formatCreateProjectParentSummary`
to the fallback path itself. Otherwise a user with Workspace Directory set to
`J:\PROJECTS` saw the summary line claim `~/orca/projects` while the field held
`J:\PROJECTS`.

Fixes #14767

* fix(settings): keep configured orca/projects paths verbatim in the create summary

The collapsed Location summary used a tail match on orca/projects, so a
configured directory like /data/orca/projects rendered as ~/orca/projects.
Scope the shorthand to usual home layouts and pin the lookalike cases.
2026-08-26 02:36:38 -07:00
Neil a27c691fdd fix(terminal): stop detached exit observers pinning evicted panes' xterm buffers (#16551) 2026-08-26 02:05:58 -07:00
Neil 2f5f5ce23c fix(ui): restore the light-mode dropdown shadow (#16570) 2026-08-25 23:43:28 -07:00
Jinwoo Hong 3c5c908451 fix(automations): scope project refs to destination host (#16552) 2026-08-25 23:32:21 -07:00
Neil f72dcb908e fix(contextual-tours): stop measuring 60 times a second while nothing moves (#16453) 2026-08-25 22:38:33 -07:00
Jinjing 933345d347 Clarify upstream divergence stats for rebased branches (#16358)
* Clarify upstream divergence stats for rebased branches

When a branch is rebased, it still tracks the pre-rebase upstream
while comparing against the new base. Move upstream arrows to the
head line to prevent them being confused with compare-base counts.

* Show upstream divergence stats independent of compare base

Measure HEAD against upstream regardless of compare-base state,
so divergence indicators stay visible even when comparison is
missing, loading, or failed. Also use cross-platform temp paths
in tests.

* Show commit counts against compare base, not upstream

Upstream divergence (↑/↓ against tracking branch) was confusing for
rebased branches — the counts appeared beside the base ref but measured
against the upstream branch. Show only the compare base count instead,
on the line that names it.

* Report branch divergence in both directions

Rebased branches are typically ahead AND behind their base; a single count
hides this case. Use symmetric range with --left-right --count to capture
both directions efficiently, then expose commitsBehind in the UI alongside
commitsAhead.

* Use semantic names for i18n keys and template variables

Rename hash-based translation keys to descriptive identifiers and replace generic value0/value1 placeholders with semantic variable names like `count` and `ref`. Improves code maintainability and makes translation strings self-documenting.
2026-08-25 22:19:04 -07:00
Jinjing 07b82340f3 Route terminal file links to sibling workspace tabs (#16544)
* fix: route terminal file links to sibling workspace tabs

Detect when a clicked file is already open in a sibling workspace and route
to that existing tab instead of creating a duplicate. Reorganizes workspace
activation to dispatch by both worktree id and execution host, allowing the
same worktree name across different remotes to be disambiguated and routed
correctly.

* test: validate terminal file link opens in correct sibling worktree

Enhance test to check both file path and active worktree ID, ensuring
the linked file opens in the intended sibling workspace.
2026-08-25 22:17:59 -07:00
Jinjing 6a3bd2a1b8 fix: keep tab-cycle shortcuts in sync with rendered group order (#16549)
Tab-cycle shortcuts (Ctrl+Tab) were getting out of sync with what the
TabBar actually renders. When a tab hydrated into the strip before
group.tabOrder was updated, it fell out of the cycle until a click.

Align keyboard cycling to use the same reconcileTabOrder pass the
TabBar uses, so the cycle always walks what the user sees. Fixes STA-3475,
particularly in remote servers where hydration timing diverges from
local.
2026-08-25 22:10:29 -07:00
Neil b57b812e72 fix(ssh): recover initial state hydration
Hydrate SSH connection states independently of best-effort tombstone labels, with bounded fanout and regression coverage.
2026-08-25 21:52:52 -07:00
Jinjing 5479bd9159 refactor(task-page): split task page into focused modules (#15163)
* rm unused files

* rm unused files

* fix(task-page): clean readiness lint findings

* Add GitLab IPC timeout wrapper and improve error handling

- Extract GitLab timeout logic into reusable `withGitLabIpcTimeout` wrapper to protect all GitLab API calls from hanging indefinitely
- Apply timeout protection to all GitLab list and fetch operations
- Add error handling for GitHub and Linear issue creation operations
- Fix event bubbling in GitHub work item row to prevent nested button clicks from opening detail page
- Remove unused `usePRReviewCellState` hook
- Consolidate redundant imports

* refactor(task-page): extract components and improve provider handling

- Add glab timeout handling (30s) to prevent IPC thread blocking
- Extract GitHub assignee/review components to dedicated files
- Improve GitLab work item row keying (repoId:id) and keyboard event handling
- Add context-aware error handling for Jira creation failures
- Refactor GitHubAssigneeAvatar to use shared GitHubUserAvatar component

* Add timeout support and error handling for GitLab operations

- Admission control times out queued work after 30s to prevent
  indefinite queueing behind saturated operations
- Mutation errors now display to users via toast instead of failing
  silently

* Consolidate workspace attachment labeling into unified utility

Extract common label-generation logic from GitHub and Linear
work-item components into a single getWorktreeAttachmentLabel
function, removing duplication across attachment types.

* Improve TaskPage accessibility, i18n coverage, and error handling

- Add missing aria-labels, roles, and semantic attributes for improved screen reader support
- Extract hardcoded UI strings into i18n system with translate() calls
- Add error handling and proper abort signal support for async operations
- Use locale-aware date formatting throughout
- Fix pagination disabled state and reviewer suggestion merging logic
- Improve async state management with proper refs and effects
- Add Textarea component import for Jira dialog

* Improve TaskPage accessibility and i18n key naming

- Add DialogTitle/Description with i18n to Linear issue dialog
- Use useId to improve aria-labelledby in GitHub selectors
- Replace hash-based i18n keys with semantic names
- Use Object.hasOwn instead of `in` for safer filter checks
- Fix PR review cell to clear input only on success

* Add missing dependencies to TaskPage hooks and useCallback/useEffect arr

Fixes exhaustive-deps warnings by adding missing setters, refs, and computed
values to dependency arrays. Refactors GitHub and Linear issue state handling
to compute values from pageData where available, with fallback to local state.
Moves imperative ref updates into useEffect to properly track dependencies.

* Fix TaskPage ref timing and null repo selection state

Treat null newIssueRepoId as a valid selection, and use useLayoutEffect to synchronize the provider context ref before paint rather than after.

* Extract Linear issue dialog components and fix popover scroll styling

- Consolidate scroll styling: apply popover-scroll-content and scrollbar-sleek classes to PopoverContent wrappers
- Remove redundant max-h-60 overflow-y-auto styles from inner picker divs
- Fix GitHub new issue repo selection to explicitly target first selected repo on fresh mount
- Correct CacheEntry import paths from store/slices/github to store/github/cache-model
- Update tests to reference extracted dialog components instead of TaskPage.tsx

* Improve GitHub task page i18n and fix issue creation edge cases

- Add i18n support to GitHub work item aria-labels (draft PR, PR, issue)
- Optimize work item row by extracting repeated source context call
- Add safety check to prevent opening detail page when issue URL is missing
- Fix dependency reference in detail opener hook
- Extend GitLab job trace timeouts (60s backend, 65s frontend) for slow logs

* Increase GitLab job trace fetch timeouts

Job traces can outlive the runner's 30-second default timeout.
Extend fetch operations to allow 60–65 seconds to complete.

* Verify sourceContext variable extraction in github row test

Update expectations to check that sourceContext is assigned to a
variable rather than called inline, matching the refactored component
implementation.
2026-08-25 21:28:21 -07:00
Brennan Benson 290f192d84 fix(updater): surface and degrade renderer shutdown checkpoint failures (STA-5505) (#16497)
* fix(updater): surface and degrade renderer shutdown checkpoint failures

The in-app updater could refuse to install with 'Renderer shutdown
checkpoint was not completed.' while the actual persist() error was
swallowed unlogged, leaving users stranded on old builds (STA-5505).

- report the swallowed persist error: console, crash breadcrumb, and a
  cross-world DOM attribute so the thrown error (and the Update Error
  dialog) names the underlying cause
- stop failing the checkpoint on sleeping-agent quit-capture errors; the
  periodic capture bounds the loss to one minute
- extend the existing durable-session degradation to full-session staging
  failures during an intentional restart, preserving the dirty-draft guard

* fix(quit): degrade and surface checkpoint-vetoed app quits (#15352)

Cmd+Q walked the same shutdown checkpoint as the updater: a persist()
throw preventDefault()ed the synthetic beforeunload and
confirmNativeWindowClose returned silently — quit accepted, nothing
logged, SIGKILL the only exit.

- run the quit checkpoint inside a window-close scope so full-session
  staging failures degrade to the durable tier for app-level closes too
  (dirty editor drafts still hard-block)
- when the checkpoint still vetoes the quit, toast the published failure
  reason instead of dying silently

* fix(updater): retry-then-degrade staging and honest capture-loss accounting

Review findings on the first pass:
- a first full-session staging failure now stays a visible, retryable
  error; only a repeat failure degrades to durable-only staging, so a
  transient IPC failure keeps its retry instead of silently dropping
  just-captured scrollback
- the sleeping-capture comment no longer overstates periodic coverage
  (periodic mode skips done panes and never stamps quit origin); the
  swallowed failure records a crash breadcrumb
- pin the exact degradable-shutdown gate expression in the source-shape
  test so rewiring it cannot pass silently

* fix(updater): arm the staging-retry flag only for degradable shutdowns

An unrelated unload's staging failure must not burn the visible first
retry of a later restart or quit.

* fix(updater): isolate shutdown checkpoint retries

Reset full-session staging retry state when a shutdown attempt is abandoned, and route Terminal-less closes through the same scoped synthetic checkpoint as mounted workspaces. Keep arbitrary thrown-value diagnostics non-throwing and localize the quit failure toast.

* fix(updater): preserve checkpoint retry lifecycle

* fix(updater): preserve empty checkpoint failure reason
2026-08-25 21:14:20 -07:00
Jinjing 5e5457983a Add clickable See more button to palette section overflow hints (#16533)
* feat: Add clickable See more button to palette section hints

Allow incremental expansion of capped sections (worktrees, tabs, projects) by clicking "See more" to reveal 20 additional entries per section. Replaces static "X more" messages with interactive expansion that resets when the query changes.

* Make soft preview See more non-clickable when no rows are hidden

- The soft preview hint's expand button is only actionable when rows are
  hidden beyond the hard cap (leadingHardOverflowCount > 0)
- When all rows already render, expanding would only reshuffle already-visible
  content without revealing anything new
- Pass undefined as the handler to prevent the click behavior in this case
- Add test case to verify the button doesn't appear when all rows fit
2026-08-25 20:23:30 -07:00
Neil 91a500712c fix(crash-reporting): see the renderer memory the heap counters never report (#16449)
* fix(crash-reporting): see the renderer memory the heap counters never report

Windows renderer crash 36048e26 arrived with 618MB of private renderer memory
and a `renderer_memory` breadcrumb reporting a 150MB V8 heap. Both numbers were
right: xterm scrollback lives in `Uint32Array` backing stores and glyph atlases
live in GPU transfer buffers, and neither is counted by `usedHeapSize`,
`mallocedMemory`, or Blink's allocator.

That made the report unanalyzable. `renderer_memory_highwater` is the crumb
carrying the subsystem census that names what grew, and it is armed on
`usedHeapSize / heapSizeLimit`. At 150MB of a 4192MB limit that ratio is 3.6% —
nowhere near the 60% mark — so the census never reached a single one of these
reports.

Measured on Windows (6 worktrees x 4 terminal tabs, 8000 lines each, this app
at 4218d505): filling 24 mounted panes moved the renderer working set from
210MB to 656MB while `usedJSHeapSize` stayed at 43MB for the whole run.

Sample the renderer's own OS footprint through `process.getProcessMemoryInfo()`
(available in the sandboxed preload) and:

- report `privateMB`, `residentMB`, and `outsideHeapMB` — the footprint minus
  everything V8 and Blink admit to holding — on every `renderer_memory` crumb;
- arm the highwater census on private-footprint marks (600MB / 1000MB) as well
  as the heap ratio, so growth outside the JS heap now carries the pane and
  store census that names it.

The footprint read is async, so a sample annotates with the previous read and
refreshes in the background: one interval of staleness is irrelevant to a
footprint trend, and awaiting it would make every sample reentrant. A shell
without the bridge, or a runtime that withholds the read, keeps sampling
exactly as before.

Retained-breadcrumb keys now distinguish the two threshold ladders; keying only
on `thresholdPct` collapsed every footprint crumb onto one slot.

crash-diagnostics.ts split at the max-lines budget: memory sampling moves to
renderer-memory-sampling.ts and the shared payload shaping to
crash-breadcrumb-data.ts.

* fix(crash-reporting): retain all renderer memory marks
2026-08-25 18:38:42 -07:00
Brennan Benson 630b71730b fix(sleep): restore agent auto-hibernation for non-Pi agents (#16430)
* fix(sleep): let non-Pi agents hibernate again, and stop repaints resetting the idle clock

Auto-hibernation could never fire for claude, codex, gemini, opencode, grok, or
any other resumable TUI agent — only pi/omp/prime-agent.

#10238 broadened the `origin: 'live'` resume anchor so every resumable agent
keeps its `--resume` handle when a turn ends. The planner rejects any pane that
already has a sleeping record, and its exemption was still Pi-only. Since the
planner's eligibility conditions are the same conditions that write the anchor,
that rejection covered every otherwise-eligible non-Pi pane.

- Split the conflated predicate. `isLiveResumeAnchorForCompletedAgent` answers
  "is this record just this pane's own live anchor?" with no vendor gate; the
  Pi-gated wrapper keeps today's exact semantics for the manual-sleep and quit
  capture call sites; `isAutomaticHibernationAllowed` carries the
  `automaticResumeBlockedBy` fence on its own.
- Fence automatic hibernation. A fenced worker must not be auto-relaunched, and
  the capture does not copy the flag — so hibernating one would erase it. Checked
  in the planner and again inside the shutdown action against freshest state,
  re-evaluated after the synchronous capture callback that could itself fence it.
- Anchor the idle clock on `stateStartedAt`, not `updatedAt`. Same-state
  repaints (OSC 9999, reconnect replays) advance `updatedAt`, restarting the
  30-minute countdown and invalidating the two-tick confirmation.
- Floor that anchor on PTY-binding age and a boundary-resolution stamp, so a
  wake or app restart still gets a full idle window instead of sleeping the
  whole backlog on the ancient timing main replays. The boundary stamp is
  written synchronously where the flag clears; sampling it per tick would miss
  a boundary written and cleared between two samples.
- Signature drops `updatedAt` and gains agent kind plus full resume identity,
  which is the change detection `updatedAt` was providing by accident.

Splits the planner into planner / pane-eligibility / snapshot to stay under the
file length limit.

* fix(sleep): drain pane teardowns sequentially

`runAgentHibernationTick` launched every confirmed shutdown unawaited, so a backlog
fanned all of them out at once. Each shutdown re-runs a full runtime-liveness sweep
(one `terminal.list` per runtime-owned worktree, 10s timeout) and then a
`terminal.stopExact` (15s timeout) — so ~100 overdue panes meant ~100 concurrent
sweeps plus ~100 concurrent stops plus interleaved persistence writes. On an SSH
runtime that is hundreds of near-simultaneous RPCs at the relay.

The fanout predates this branch, but auto-hibernation could not fire for non-Pi
agents, so it never ran at scale. Restoring eligibility is what exposes it.

Awaiting each teardown also makes `tickInFlight` real: it was cleared in the
`finally` as soon as the promises were launched, so it never covered the drains it
was meant to guard. Each candidate still re-validates against a fresh plan at its
own turn, so a slow drain cannot act on stale confirmation, and per-candidate
failures are already caught so one stuck teardown cannot abort the rest.

* perf(sleep): scope hibernation rechecks to pane owner
2026-08-25 18:22:53 -07:00
Jinwoo Hong c8567eb16e fix(sidebar): preserve hidden rows in manual order (#16488) 2026-08-25 16:46:42 -07:00
Brennan Benson efa3b972c2 fix(native-chat): prevent duplicate mobile prompt echoes (#15656) 2026-08-25 15:47:40 -07:00
Brennan Benson 29f1f4e545 test(perf): warm palette matcher before timing
Warm the palette matcher before measuring steady-state p95 performance.
2026-08-25 15:42:13 -07:00
Jinwoo HongandJinwoo-H a9781a4118 STA-4150: client-hosted remote browser (consolidated) (#15448)
Co-authored-by: Jinwoo-H <jinwoo@stably.ai>
2026-08-25 15:36:51 -07:00
Brennan Benson 98bdd653ab fix(native-chat): stop the spinner on a not-yet-flushed transcript (#16493)
* fix(native-chat): stop the spinner on a not-yet-flushed transcript

A brand-new agent session can take minutes to write its first JSONL line,
and one that is never prompted never writes it at all. The host emitted no
stream frame until the file resolved, so every native-chat client sat on a
bare spinner with the composer enabled but the transcript blank -- forever,
in the never-prompted case.

The resolve poll now reports the transcript as pending after a short grace,
and both host handlers emit a `pending: true` snapshot. It is deliberately
not a plain empty snapshot: an empty window sold as a settled read would
capture over retained history and unblock consumers that require a
trustworthy transcript (the launch-draft adoption would re-offer a prompt
the agent may already have taken).

Clients render it as the "start a chat" empty state while keeping the read
unsettled -- `awaiting-transcript` on mobile, an `awaiting` read phase on
desktop, which also stops the seed loop expiring into an error card for a
session that is simply new. New optional field only, so older clients
ignore it and still stop spinning.

* fix(native-chat): negotiate pending transcript frames
2026-08-25 15:06:47 -07:00
Brennan Benson bc98655a39 fix(remote): stop an empty host inventory settling the mirror (#16414)
* fix(remote): stop an empty host inventory settling the mirror

An inventory with zero published snapshots satisfied
`settles.length === fullInventory.publishedSnapshotCount` as `0 === 0` and
fired the environment-wide host-mirror verdict with no host evidence behind
it. A live relay/SSH-paired host answers exactly that until its renderer's
first publish, so the drained resume sweep forked a second agent onto a PTY
the host was still running.

Gate that one case behind a `terminal.list` readiness probe: it reads the PTY
controller, not the session-tab mirror, so it sees live PTYs the mirror has
not published. Only "no terminals" settles; live or unverifiable leaves
waiters parked for the next inventory or per-worktree frame — a host with
genuinely zero terminals still settles, so panes do not park forever.

Fixes STA-5377

* fix(remote): fence host readiness probes by generation

* fix(remote): preserve legacy terminal probe fallback
2026-08-25 10:59:25 -07:00
Jinjing 32df073e44 fix(browser): focus unified tab on browser page palette activation (#16366)
* fix(browser): focus unified tab on browser page palette activation

When activating a browser page from the palette, find and focus the
corresponding unified tab before setting active state. Ensures the
tab group receives focus. Also increase e2e test timeouts to improve
stability on slower runners.

* test(e2e): read latest restored terminal frame

* Fail browser page activation when unified tab is missing

Without a unified tab, the workspace can't render in the pane. Reporting
success leaves the previous tab on screen. Fail the activation to prevent
this confusing state.
2026-08-25 04:00:59 -07:00
Jinjing e361da7fb7 Deleting skill (#16357)
* Add skill deletion with cross-platform transaction safety

Implements end-to-end skill removal with placement enumeration, dependency guards, and transactional recovery. Covers native, WSL, and remote hosts; users can delete canonical directories and alias placements (symlinked directories or files) in a single atomic batch. Includes UI selection flow, preview, confirmation, and results band. Block reasons (bundled, plugin, unowned, stale) gate deletions that would fail or contradict user intent.

* Organize IPC handlers into module subdirectories

Move register-core-handlers and skill-delete-ipc-handlers into
dedicated subdirectories for improved code organization and to
reduce the flat structure in src/main/ipc/.

* Make skill deletion recovery transactions idempotent

Defer journal cleanup until both staging removal and receipt cleanup succeed, leaving the journal in place for startup to retry if either operation fails. This ensures the recovery process is safe to run multiple times without leaving partially-deleted skills.

* Consolidate skill-delete files into dedicated module

Reorganize skill deletion functionality into a modular structure under
`src/main/skills/skill-delete/` with simplified file names. Remove the
redundant `skill-delete-` prefix from file names since they now live in
the dedicated directory. Update all import paths throughout the codebase
to reflect the new structure, including imports from IPC handlers and
RPC methods.

* Fix broken import paths and add deletion robustness improvements

Import paths using `..//'` were invalid and broken. Replace with explicit
module names (`skill-discovery-sources`, `skill-install-filesystem`, etc.)
to clarify dependencies.

- Bind WSL filesystem methods to preserve `this` context
- Keep recovery journal when rollback rename fails, so startup can retry
- Skip symlink-based tests on Windows where they cannot run
- Only treat ENOENT/ENOTDIR as empty directories; propagate other errors
- Fix cross-platform path parent calculation to handle drive roots
- Replace shared constant with localized string for user-facing message
- Use `runProcess` for WSL integration test instead of bare `execFile`

* Add batch limit for skill deletion and improve host availability checkin

- Limit concurrent deletions to prevent remote host overload
- Add retry logic for capability probing to handle transient unavailability
- Add reprobe() method to recheck capability after errors or user refresh
- Fix status logic: receipt cleanup is best-effort, completion depends only on content removal
- Improve error message for unreachable hosts
2026-08-25 03:58:48 -07:00
NeilandSeongho.Bak fcf55f2d68 fix(terminal): stop Orca mangling the OMP/Pi title it writes itself (#16381)
* fix(terminal): collapse identity group in the title churn signature

Replaces the ingest-time title rewrite from #16373 with a non-destructive
fix at the actual cause.

The churn suppressor `isDecorativeAgentTitleFrameChange` keyed on the
literal label, so `working:OMP` and `working:Pi` compared unequal and every
alternating frame from a wrapped harness committed a store patch. #16373
made the labels agree by rewriting the stored title to the tab's launch
owner — but `runtimePaneTitlesByTabId` is also the Windows Shift+Enter
byte-encoding input, so normalizing at ingest destroyed evidence other
consumers read (fixed separately in #16376).

Collapse the identity group inside the signature instead. Which member of
a group a frame names is decoration, exactly like the spinner glyph the
signature already strips, so frames compare equal without touching what is
stored. Suppression now changes only WHETHER a frame commits, never WHAT
it says.

Also fixes the flap under a multiplexer (#8032): the collapse runs over
wrapper segments, so "zsh | ⠋ Pi" and "zsh | ⠙ OMP" compare equal, which
the anchored owner-relabel in #16373 never matched.

Reverts the store changes from #16373 and drops the helper it added.

Co-authored-by: Seongho.Bak <49228032+psh4607@users.noreply.github.com>

* fix(terminal): fold only bare identity frames into the group token

A legacy "π - <session> - <cwd>" title is Pi-compatible too, so folding
every profile match collapsed two different sessions to the same signature
and suppressed the change outright — reintroducing #16093 through the
churn signature.

Fold only exact bare identity frames, matched per wrapper segment, so
semantic session titles keep comparing on their own text.

Co-authored-by: Seongho.Bak <49228032+psh4607@users.noreply.github.com>

* docs(terminal): correct the flap diagnosis in the repro header

Verified against the OMP source: it emits only π-glyph frames
(`DEFAULT_TERMINAL_TITLE = "π"`, title-generator.ts:25), and on an
Orca-hosted pane its native titler cedes to Orca's own injected extension,
which writes `⠋ π - <session> - <cwd>`.

So OMP emits neither "OMP" nor "Pi". Both flap sides are Orca's:
"OMP" from driveSyntheticTitleFromHook, "Pi" from normalizeTerminalTitle
collapsing our own extension's output to a hardcoded literal.

The prior header credited the wrapped harness for frames it never sends,
which is the same wrong narrative that produced eight fixes at eight
layers. No behavior change.

* fix(terminal): stop Orca mangling the OMP/Pi title it writes itself

Verified against the OMP source: it emits only π-branded frames
(`DEFAULT_TERMINAL_TITLE = "π"`, title-generator.ts:25), and on an
Orca-hosted pane its native titler cedes to Orca's OWN injected extension,
which writes `π - <session> - <cwd>` / `⠋ π - <session> - <cwd>` at 80ms.

So neither flapping string came from OMP. Orca made both:
  "Pi"  — normalizeTerminalTitle collapsing our extension's output to a
          hardcoded literal, discarding the session name and cwd (#16093)
  "OMP" — driveSyntheticTitleFromHook injecting over it every 80ms

Fixed at the source:
- normalizeTerminalTitle canonicalizes only the rotating braille frame and
  keeps the rest, in both spinner positions and through a multiplexer
  prefix (#8032). Status still round-trips through normalization.
- detectAgentStatusFromTitle reads the π state separator, so `π ! <label>`
  is permission instead of the blanket idle that hid a blocked agent.
- normalizeCompatibleAgentTitleForOwner swaps only the brand for the
  owner's label, so a pane still reads as its launch owner (#6689, #7633,
  #9077) without losing the session text.
- pi/omp set synthesizeWorkingTitle: false — the agent animates its own
  working title. Terminal states still synthesize; they carry the pane's
  agent identity downstream.

Reverts the ingest-time title rewrite from #16373, whose normalization of
runtimePaneTitlesByTabId also changed Windows Shift+Enter bytes (#16376).

Co-authored-by: Seongho.Bak <49228032+psh4607@users.noreply.github.com>

* fix(terminal): match the state separator only in exact profile casing

The separator check runs on every title, so `omp - deploy notes` and
`pi - refactor the parser` read as an idle agent. The owner rewrite only
ever emits the exact profile labels, so dropping case-insensitivity keeps
`OMP - tmp` classifying while ordinary prose stops matching.

Co-authored-by: Seongho.Bak <49228032+psh4607@users.noreply.github.com>

* test(terminal): pin one real OMP turn to two committed patches

Drives 30 working frames as Orca's injected extension emits them plus the
idle transition, and asserts what survives the churn gate. Before the fix
every frame alternated "⠋ Pi"/"⠋ OMP" and each one committed — ~12 store
patches per second on a working tab.

Co-authored-by: Seongho.Bak <49228032+psh4607@users.noreply.github.com>

* fix(terminal): carry the permission guard inside the separator reader

`-` is both a π state separator and the delimiter in the synthetic
permission label, so `OMP - action required` read as idle. It resolved
correctly only because detectAgentStatusFromTitle happens to check the
synthetic label first — and the separator fn is exported, so a direct
caller inherited the bug.

Also pins the owner rewrite's fixed-point property, which holds only
because getAgentLabel does not tokenize omp/pi, and corrects a comment
that overstated how tightly the brand swap is scoped.

Co-authored-by: Seongho.Bak <49228032+psh4607@users.noreply.github.com>

* docs(terminal): name the flag the code actually sets

The suite header cited `synthesizeTerminalTitle: false`; the profiles set
`synthesizeWorkingTitle: false`. The distinction is the whole reason the
narrower flag was chosen — terminal-state frames still carry the pane's
agent identity downstream — so the wrong name buried the rationale.

Co-authored-by: Seongho.Bak <49228032+psh4607@users.noreply.github.com>

---------

Co-authored-by: Seongho.Bak <49228032+psh4607@users.noreply.github.com>
2026-08-25 02:28:08 -07:00
Neil 8217e6838f refactor runtime contracts and web transports (#16197)
* refactor runtime contracts and transports

* test(web): repoint two-phase timeout seam at the transport that now owns call()
2026-08-25 00:59:29 -07:00
Neil d456549c22 refactor runtime Git bridges (#16189)
* refactor runtime git bridges

* fix(test): avoid computed namespace import access in runtime Git client contract test
2026-08-25 00:54:43 -07:00
Neil f482725355 refactor terminal coordination modules (#16335)
* Refactor terminal coordination modules

* preserve terminal completion and stale-connect guards

* restore pre-spawn E2E barrier and stale-connect check order in ipc-pty-connect

* restore merge-base title-working replay and stamped-tail delete semantics

* fix(terminal): merge the duplicated shortcut-matching import

Two adjacent imports of the same module tripped oxlint's
no-duplicate-imports under --deny-warnings. Import-only; no behavior change.
2026-08-25 00:53:54 -07:00
Neil 5ebc4bb2bc refactor(renderer): split repos store slice (#16191)
* refactor(renderer): split repos store slice

* test(store): align repos/folder-workspace characterization tests with main's owner-scoped delete + host-qualified visit recency

* fix(composer-state): repoint RepoUpdate import to repos/repo-state after split
2026-08-25 00:42:59 -07:00
Neil a782935ab3 refactor(checks): split panel responsibilities (#16186)
* refactor(checks): split panel responsibilities

* fix(checks): consolidate comment audience import

* fix(checks): handle open review state exhaustively

* fix(checks): restore merge-base check presentation values

The split into check-presentation.tsx silently changed icon, opacity, and
PR-state token values. Restore them verbatim.
2026-08-25 00:33:36 -07:00
Neil e0bc4e1126 refactor(editor): split notebook, editor, and external-watch surfaces (#16143)
* refactor(editor): split editor and watch surfaces

* fix(editor): revert behavior changes smuggled into the surface split

Restore merge-base React keys in IpynbCellOutputs: the content-identity keys
JSON.stringify'd every output value, including raw base64 image payloads, on
every keystroke.

Collapse the duplicated lazy() declarations into editor-lazy-views so each
viewer keeps a single React.lazy identity across the extracted surfaces.
2026-08-25 00:30:47 -07:00
hjkwonandClaude Opus 5 8e8c6d7ed2 fix(linear): make new-issue dialog popovers scrollable (#16382)
* fix(linear): make new-issue dialog popovers scrollable

`[data-slot='popover-content']` already caps every popover to
`--radix-popover-content-available-height`, but PopoverContent's base class is
`overflow-hidden`. A team list taller than that cap is therefore clipped at the
window edge with no scrollbar and no way to reach the entries past the cut.

The dialog's other attribute popovers had an inner max-h-60 box, but none of the
six carried the popover-scroll-content / popover-wheel-scroll marker that
popover.tsx's wheel shim needs, so Radix's dialog scroll-lock swallowed the wheel
there too.

Move all six to the popover-scroll-content pattern already used by
LinearItemDrawer, JiraIssueWorkspace, and github-item-dialog: it re-declares the
cap as min(15rem, available-height) and adds overflow-y: auto, and the class name
opts the content into the wheel shim.

Measured on the team switcher with 25 teams:
  before  max-height 611px, overflow-y hidden, 609 of 735px visible
  after   max-height 240px, overflow-y auto,   scrollTop reaches 497

The inner max-h-60 boxes are dropped because stacking them under the outer cap
creates nested scrollers whose combined height exceeds it, leaving the bottom of
each list unreachable.

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

* test(linear): match the inner-scroller classes regardless of order

The previous assertion pinned one exact class order, so reintroducing the
wrapper as `scrollbar-sleek overflow-y-auto max-h-60` slipped through. Collect
the section's `<div>` classNames and check the three tokens as a set instead.

Scoped to wrapper divs on purpose: the dialog's description textarea caps its
own growth with those same classes and is not a popover child, so a plain
whole-section match flags it as a false positive.

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 00:01:54 -07:00
Brennan Benson 656e4891de fix(ssh): restore the reconnect model-paint gate dropped by #15166 (#16361)
* fix(ssh): restore the reconnect model-paint gate dropped by #15166

#15166 split pty-connection.ts and dropped the "paint from main's model on SSH
reconnect" half of the reattach gate that shipped in v1.4.188 (#14844), leaving
only the park-reveal half. A non-park SSH reconnect has repainted from the
~100KiB relay tail ever since, which cannot rebuild a full-screen frame whose
start it no longer holds.

Restores followsDirectSshReconnect (PENDING-only retry read), reconnectMayUseModel,
the exited-transition veto computed before the probe, and the kitty scanReplay
layered after the snapshot baseline. Adds a call-site test over
createReattachPayloadHandlers, because the surviving pure-function test stayed
green through the entire removal.

Fixes STA-5395

* fix(ssh): restore empty-tail reconnect snapshots
2026-08-24 23:49:18 -07:00
Neil 5c3df831b7 refactor composer and jira slice modules (#16196) 2026-08-24 23:49:08 -07:00
Neil 32ffdd2b9a refactor(renderer): split composer state (#16182)
* refactor(renderer): split composer state

* fix(renderer): satisfy composer static analysis

* test(renderer): migrate composer boundary contracts

* fix(composer): restore project group reset effect

Revert read-side mask back to the merge-base state clear so a momentarily
unavailable host permanently drops the folder group instead of silently
retargeting Create when the host reappears.
2026-08-24 23:14:42 -07:00
Wooseong Kimandm4air 60a3fd8873 fix(i18n): localize the keep-awake corner chip (#14775)
* fix(i18n): localize the keep-awake corner chip

Route the status-bar keep-awake chip through the shared Agents copy
helpers and add missing locale entries for chip-only words.

Fixes #14490

* test(i18n): restore previous language after keep-awake locale suite

* test(i18n): render component in localization tests instead of static che

Converts the keep-awake localization test from static source-code validation to actual component rendering with React Testing Library, providing more reliable verification that the UI displays correctly across all supported languages. Improves translated descriptions for consistency and accuracy.

* test(i18n): add aria labels and descriptions to localization test

- Adds missing localization keys to test data for Spanish, Japanese, Korean, and Simplified Chinese
- Updates test assertions to verify `ariaLabel`, `onDescription`, `autoDescription`, and `offDescription` are properly translated
- Completes localization coverage for the keep-awake corner chip component

---------

Co-authored-by: m4air <m4air@m4airs-Air.localdomain>
2026-08-24 22:46:29 -07:00
Neil c4988fa81c refactor workspace lifecycle modules (#16334)
* refactor(workspaces): split lifecycle modules

* preserve workspace cleanup consent contract

* restore workspace delete shortcut hint in context menu view

* restore host-qualified visit recency and viewed-candidate predicate

* test(cleanup): pin the viewed-mark upgrade path and host-qualified visit reads

Two invariants a refactor broke in this PR, both silent:

- viewed marks are persisted, so gating `shouldPreserveCleanupInspection` on any
  newer field voids the grace period for every entry written by an older build
- visits are stamped under `${hostId}|${worktreeId}` whenever the host is known
  (the normal case, including 'local'), so a bare map[worktreeId] read misses
  every modern entry and yields 0, disabling the recent-visible-context blocker

Verified discriminating: reintroducing each bug fails exactly its own test.
2026-08-24 22:44:08 -07:00
Neil 73f709217b test(linear): pin project out of the hydration-preserved field set (#16364)
Linear list issues never carry `project` (only getIssue maps it, via
includeProject: true). If `project` joins EDITED_LINEAR_ISSUE_FIELDS, an edit
made while getIssue is in flight overwrites the hydrated project with the list
issue's undefined, permanently blanking it — the sidebar shows 'Add to project'
and LinearIssueSubIssues then files sub-issues with projectId: null instead of
inheriting the parent's project.

Verified discriminating: re-adding 'project' to the field set fails these.
2026-08-24 22:43:42 -07:00
Jinjing 0ac68f0d4b Add background color to search button in sidebar (#16130)
Applies a subtle background color to the search button to make it more visually distinct.
2026-08-24 22:42:11 -07:00
Jinjing 90743cf17b feat: add workspace search and improve installation target UI (#16380)
- Add searchable Combobox for workspace selection with label and type filtering
- Redesign agent picker from collapsible to popover with better visual hierarchy
- Restructure skill install review screens with card-based sections
- Add comprehensive tests for workspace search and agent selection
2026-08-24 22:41:06 -07:00
NeilandSeongho.Bak 4a57cfac9a fix(terminal): give OMP Pi's Windows Shift+Enter encoding (#16376)
OMP wraps Pi's TUI, so Shift+Enter bytes land in a Pi reader that decodes
CSI-u. The omp profile had no `windowsShiftEnterEncoding`, so it fell back
to Esc+CR — which submits instead of inserting a newline (#9703).

This was latent while an OMP pane's stored title could read either "Pi" or
"OMP" depending on which interleaved frame committed first. Pinning the
title to the launch owner (#16373) made it deterministically "OMP", so the
Windows Shift+Enter fallback now always resolves `omp` and always picks the
wrong encoding.

`prime-agent` already carries this entry for the identical reason.

Co-authored-by: Seongho.Bak <49228032+psh4607@users.noreply.github.com>
2026-08-24 22:27:45 -07:00
NeilandSeongho.Bak 3b6eb03349 fix(terminal): stop OMP tab title flapping between OMP and Pi (#16373)
* fix(terminal): stop OMP tab title flapping between OMP and Pi

OMP wraps Pi, and both share the `pi-compatible` title-identity group. Two
writers publish frames for the same pane under different labels: main's
synthetic spinner injects "<frame> OMP" every 80ms, while the wrapped Pi
harness emits its own "Pi" frames.

`isDecorativeAgentTitleFrameChange` keys on `status:textWithoutSpinner`, so
`working:OMP` and `working:Pi` read as meaningful changes. The alternation
defeated spinner-churn suppression entirely: every 80ms frame committed a
store patch plus a runtime-graph sync, on both the tab-title and
runtime-pane-title paths.

Pin same-group identity frames to the tab's launch owner at both store
choke points, reusing the existing owner-normalization helper already
applied on the sidebar, remote-sync, and mounted-pane paths.

The relabel is scoped to bare identity frames ("⠋ Pi", "Pi ready"); a
semantic session title ("π - <session> - <cwd>") carries text no agent
profile can reproduce and is left untouched, so this does not reintroduce
the generic-label complaint in #16093.

* fix(terminal): scope owner relabel to cross-identity frames

A frame that already names the tab's own agent carries authoritative status
wording, so relabeling it restated bare "Pi" as "Pi ready" and changed a
Pi-owned tab that never flapped. Only relabel when the frame names a
different member of the identity group.

Also fixes the repro suite's types against the project typecheck.

Co-authored-by: Seongho.Bak <49228032+psh4607@users.noreply.github.com>

---------

Co-authored-by: Seongho.Bak <49228032+psh4607@users.noreply.github.com>
2026-08-24 22:16:22 -07:00