Commit Graph
2191 Commits
Author SHA1 Message Date
JinjingandOrca 333cf6fd6c update (#1692)
Co-authored-by: Orca <help@stably.ai>
2026-05-10 23:44:03 -07:00
Brennan BensonandOrca 1041ab4f7c feat(agent-hooks): shared listener + relay adapter (PR 1/N for SSH agent status) (#1678)
* feat(agent-hooks): introduce relay wire envelope + connectionId stamping

Adds the shared `agent-hook-relay.ts` module with the `agent.hook` JSON-RPC
notification envelope, the `agent_hook.requestReplay` /
`agent_hook.installPlugins` method names, and the
`ORCA_FEATURE_REMOTE_AGENT_HOOKS` flag helper. Promotes `AgentHookSource` to
`shared/` so the relay can import it without dragging Electron in.

Threads a `connectionId: string | null` field through `AgentHookEventPayload`,
the `agentStatus:set` IPC contract, and the renderer-bound preload listener.
Local hook posts stamp `null`; the relay-forwarded path will stamp from `mux`
identity in a later commit. Renderer uses the stamp for stale-event filtering
when an SSH connection tears down with notifications still in flight.

See docs/design/agent-status-over-ssh.md §1, §5, §8 (commit #1).

Co-authored-by: Orca <help@stably.ai>

* refactor(agent-hooks): extract shared listener; add relay-side adapter

Extracts the listener internals (request parsing, payload normalization,
endpoint-file writing, per-CLI extractors, warn-once Sets, slowloris timer
helper, request size cap, paneKey caches) from `src/main/agent-hooks/server.ts`
into a new transport-agnostic `src/shared/agent-hook-listener.ts`. The shared
module uses only Node builtins (no Electron) so it is safe to import from
`src/relay/`.

Adds `src/relay/agent-hook-server.ts` — a thin HTTP-loopback adapter that
wires the shared listener to a `forward(envelope)` callback so `relay.ts` can
re-emit each parsed payload as an `agent.hook` JSON-RPC notification on the
existing SshChannelMultiplexer. The adapter owns:

- 127.0.0.1:0 socket + bearer-token auth, identical shape to the local server
- per-paneKey last-payload cache + replayCachedPayloadsForPanes() for the
  request-driven replay path used after `--connect` reattach (see §5 Path 3)
- clearPaneState(paneKey) for PTY-exit eviction (symmetric with local server)
- buildPtyEnv() / endpoint-file writing for relay-spawned PTYs

Orca's `AgentHookServer` is now a ~200-LoC adapter over the shared listener
that owns the IPC fanout, listener replay, and `ingestRemote(envelope, connId)`
entry point that bypasses the HTTP path for relay-forwarded events.

See docs/design/agent-status-over-ssh.md §3, §8 (commit #2).

Co-authored-by: Orca <help@stably.ai>

* fix(preload): expose connectionId on agentStatus.onSet type

src/preload/index.ts already passes through `connectionId?: string | null`
from main, but the PreloadApi declaration in api-types.ts was missing the
field. Align the type with the runtime contract so renderer call sites
can read connectionId without an `as` cast.

Co-authored-by: Orca <help@stably.ai>

* fix(agent-hooks): harden ingestRemote + relay replay; review-driven cleanup

- ingestRemote: re-run normalizeAgentStatusPayload at trust boundary;
  trim+validate connectionId/paneKey/tabId/worktreeId
- relay: preserve source/env/version through replay via sidecar map;
  drop sourceFromAgentType fallback that mis-tagged unknown agents
- shared listener: exhaustive switch+never on AgentHookSource dispatch
  chains; extractPromptText returns trimmed values; export MAX_PANE_KEY_LEN
- preload: tighten connectionId from optional to required (always sent)
- main IPC: reorder spread so explicit envelope fields win on collision

Co-authored-by: Orca <help@stably.ai>

* chore(docs): drop agent-status-over-ssh design doc from PR

The design RFC was useful for authoring this PR series but doesn't belong
in-tree — keeping it here would freeze line-number references and design
prose against future churn. Folding it into the PR description instead.

Co-authored-by: Orca <help@stably.ai>

* chore(agent-hooks): widen ingestRemote type for env/version (PR2 prep)

Declares `env?: string` and `version?: string` on the `ingestRemote` envelope
parameter so PR2 only needs to add the `warnOnHookEnvOrVersionMismatch`
callsite, not also widen the type. The fields are forwarded verbatim from
the agent CLI POST body on the remote and let Orca's warn-once cross-build
/ dev-vs-prod diagnostics fire identically on remote-sourced events.

Type-only addition; no runtime consumer in this PR.

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Orca <help@stably.ai>
2026-05-10 21:57:48 -07:00
Jinwoo HongandOrca c88287fa9a Fix Codex account auth read-back guard (#1629)
* Fix Codex account auth read-back guard

Co-authored-by: Orca <help@stably.ai>

* Guard Claude auth read-back identity

Co-authored-by: Orca <help@stably.ai>

* Fix Claude auth read-back test on Linux

Co-authored-by: Orca <help@stably.ai>

* Require positive Codex auth identity match

Co-authored-by: Orca <help@stably.ai>

* Address auth read-back review gaps

Co-authored-by: Orca <help@stably.ai>

* Harden managed auth read-back state machine

Co-authored-by: Orca <help@stably.ai>

* Harden managed auth token read-back

Co-authored-by: Orca <help@stably.ai>

* Isolate managed Codex launch homes

Co-authored-by: Orca <help@stably.ai>

* Keep managed Codex homes in sync

Co-authored-by: Orca <help@stably.ai>

* Revert "Keep managed Codex homes in sync"

This reverts commit bce1ecdc38.

* Revert "Isolate managed Codex launch homes"

This reverts commit 826a6c4773.

* Clarify shared Codex auth routing comment

Co-authored-by: Orca <help@stably.ai>

* Route Codex auth read-back by identity

Co-authored-by: Orca <help@stably.ai>

* Route Claude auth read-back by identity

Co-authored-by: Orca <help@stably.ai>

* Handle corrupt Codex auth snapshots

Co-authored-by: Orca <help@stably.ai>

* Reject stale cold-start auth read-back

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Orca <help@stably.ai>
2026-05-10 20:39:38 -07:00
Brennan BensonandOrca 1f43346c5f fix(resource-usage): hydrate pty-registry at boot; render · remote only for SSH repos (#1667)
* WIP: Changes before auto-review fixes

Co-authored-by: Orca <help@stably.ai>

* fix: address auto-review-fix-multi-agent findings

- Replace local ORCA_WORKTREE_ID_SEPARATOR with shared WORKTREE_ID_SEPARATOR
- Make hydrateLocalPtyRegistryAtBoot idempotent (one-shot per process,
  but stays retry-eligible until daemon provider is available)
- Strengthen daemon-pty-adapter strict-parser test to actually exercise
  the new short-circuit (test would have passed under the old loose
  parser too without the change)
- Add eslint-disable max-lines directive to oversized merge test file

Co-authored-by: Orca <help@stably.ai>

* chore: archive auto-review context to .context/

Co-authored-by: Orca <help@stably.ai>

* fix: address auto-review-fix findings

Drop the destructive reconcileOnStartup call from boot-time PTY registry
hydration: a transient listRepoWorktrees failure (returns [] and only
warns) would otherwise let the reconcile pass kill live local sessions.
The boot path is now read-only against the daemon — listSessions() only.

Also: tighten parsePtySessionId to reject degenerate `::` halves; replace
stale pty.ts:1005 references and a misleading local-unknown comment in
the hydrate module; narrow Store dependency to Pick<Store, 'getRepos'>;
log adapter listSessions failures instead of silently swallowing them;
re-anchor design-doc references on stable symbols and align §1b/§1c/§1d
with the implementation.

Co-authored-by: Orca <help@stably.ai>

* docs(resource-usage): update remote badge spec

Co-authored-by: Orca <help@stably.ai>

* test(resource-usage): cover boot hydration failure modes + warm-reattach e2e

Adds the regression coverage flagged in PR #1667's test plan that wasn't
already locked down.

vitest (`hydrate-local-pty-registry.test.ts`):
  - daemon offline at first call → no-op, hasHydrated stays false so a
    later macOS dock re-activation can retry.
  - listSessions rejection caught and logged, does not throw.
  - pid-write ordering: a pre-existing registry entry with pid=12345 is
    not clobbered by a stale `pid: null` from listSessions (§1d).
  - SSH-gate: a session whose repo has a non-null connectionId stays out
    of the registry, mirroring the spawn-time gate in pty.ts.
  - Happy-path: a local session is registered with the daemon's pid.

Playwright e2e (`resource-usage-warm-reattach.spec.ts`):
  Full quit→relaunch cycle against the same userDataDir; asserts that
  on the second launch the snapshot includes the warm-reattached PTY
  with a real pid before any pane mount, and that the seeded repo
  resolves as local (no connectionId). Mirrors the existing
  terminal-restart-persistence pattern.

Co-authored-by: Orca <help@stably.ai>

* fix(test): satisfy Pick<Store, 'getRepos'> in hydrator vitest

CI typecheck failed because FakeStore's getRepos returned objects missing
Repo's required fields (path, displayName, badgeColor, addedAt). Fill with
placeholder values; the hydrator only reads id + connectionId, but the
type signature still has to line up.

Co-authored-by: Orca <help@stably.ai>

* chore(resource-usage): drop bug-doc files; strip dead doc refs from comments

Remove docs/resource-usage-remote-mislabel.md (new in this PR) and revert
docs/resource-usage-merge-spec.md to the PR-base state. Strip the
matching `docs/...md §N` pointers from code/test comments, keeping the
surrounding "why" explanations intact so readers still get the
warm-reattach mislabel context.

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Orca <help@stably.ai>
2026-05-10 17:17:52 -07:00
Brennan BensonandOrca 98b15aeabc feat(telemetry): instrument on_path:false triage on onboarding_agent_picked (#1674)
* feat(telemetry): instrument on_path:false triage on onboarding_agent_picked

Adds path_source and path_failure_reason to onboarding_agent_picked so the
~30% on_path:false rate on dashboard 1562016 can be split between shell
hydration failures and genuinely-not-on-PATH cases before picking a fix.
See docs/agent-on-path-detection.md.

Co-authored-by: Orca <help@stably.ai>

* fix(telemetry): close PathSource compile-time-sync hole

Add `_PathSourceSync` guard mirroring `_PathFailureReasonSync` so adding
a new `PathSource` value to the alias without updating the schema (or
vice versa) fails the build. Without it, drift would silently drop
`onboarding_agent_picked` at the strict validator. Also replace stale
line-number references in docs/agent-on-path-detection.md with named
function/handler references that survive future edits.

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Orca <help@stably.ai>
2026-05-10 15:47:14 -07:00
Brennan BensonandOrca 8977c7e917 feat(telemetry): track agent_hook_install_failed per agent (#1668)
* feat(telemetry): track agent_hook_install_failed per agent

Replaces the closure-style installer loop in `src/main/index.ts` with a
labelled `runManagedHookInstallers` so each catch can attribute the
failure to its agent. Adds the `agent_hook_install_failed` event +
`hookInstallAgentSchema` enum (claude/codex/gemini/cursor) and a unit
test pinning fail-open semantics, label routing, and the 200-char
error_message truncation.

Co-authored-by: Orca <help@stably.ai>

* fix(telemetry): harden agent-hook installer fail-open

- describeError always returns a string (JSON.stringify can return
  literal undefined for throw undefined / Symbol / function, which
  would crash the catch handler before track fires)
- wrap track() in inner try/catch so a telemetry-side throw can't
  abort the installer loop
- dedupe AGENT_HOOK_TARGETS into one tuple in agent-hook-types so
  the IPC AgentHookTarget type and hookInstallAgentSchema can't drift
- regression tests for object/undefined throws and track-throws

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Orca <help@stably.ai>
2026-05-10 14:41:40 -07:00
Neil 41b4a2bc50 Use artifact titles for created workspaces (#1643) 2026-05-09 18:51:07 -07:00
Neil 4789a3cd51 Update titlebar app name controls (#1644) 2026-05-09 18:48:53 -07:00
Jinwoo HongandOrca b023c5713e Polish rich markdown editor interactions (#1609)
Co-authored-by: Orca <help@stably.ai>
2026-05-08 23:28:01 -07:00
Brennan BensonandOrca 318e2b4c2c feat(telemetry): onboarding cohort + extension events (#1608)
Co-authored-by: Orca <help@stably.ai>
2026-05-08 19:57:18 -07:00
JinjingandOrca 11b362c92d feat(worktree): optimistic create with reconciled base status (#1600)
Surface worktree creation immediately and reconcile remote base state
asynchronously, emitting drift/conflict events as fetches complete.

Co-authored-by: Orca <help@stably.ai>
2026-05-08 14:59:23 -07:00
JinjingandOrca e0851ec722 feat: onboarding flow for new users (#1596)
* wip

* WIP: Changes before auto-review fixes

Co-authored-by: Orca <help@stably.ai>

* WIP: Changes before auto-review fixes

Co-authored-by: Orca <help@stably.ai>

* WIP: Changes before auto-review fixes

Co-authored-by: Orca <help@stably.ai>

* fix: address auto-review findings (iteration 1)

Co-authored-by: Orca <help@stably.ai>

* fix: address auto-review findings (iteration 2)

Co-authored-by: Orca <help@stably.ai>

* fix: archive review context and improve agent detection on wizard mount

Co-authored-by: Orca <help@stably.ai>

* fix: address CI lint failures and split use-onboarding-flow.ts

Co-authored-by: Orca <help@stably.ai>

* fix: mock ./onboarding in register-core-handlers test

Co-authored-by: Orca <help@stably.ai>

* fix: also toggle light class on documentElement so onboarding e2e theme wait resolves

The onboarding e2e calls waitForFunction(() => classList.contains('dark') || classList.contains('light')) before snapshotting the starting theme. applyDocumentTheme only toggled 'dark', so on a host that resolves system to light the wait timed out (CI Linux headless). Toggle 'light' as the inverse class so consumers can observe the resolved theme symmetrically; Tailwind keys only on 'dark' so styling is unchanged.

Co-authored-by: Orca <help@stably.ai>

* fix: add braces to Landing menu close-on-outside-click handler

oxlint config requires braces for all if statements.

Co-authored-by: Orca <help@stably.ai>

* chore: trigger CI

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Orca <help@stably.ai>
2026-05-08 14:17:50 -07:00
Brennan BensonandOrca 2050fa87a0 feat(source-control): send all notes to agent in a new terminal tab (#1568)
Co-authored-by: Orca <help@stably.ai>
2026-05-08 13:52:08 -07:00
Jinjing 2dcff0687f fix: rename sidekick to pet (#1586) 2026-05-08 13:04:09 -07:00
Brennan BensonandOrca 002e2acc38 feat(telemetry): onboarding-funnel events + nth_repo_added cohort (#1591)
Co-authored-by: Orca <help@stably.ai>
2026-05-08 10:50:43 -07:00
Brennan BensonandOrca 5a2a32b614 feat(agent-dashboard): default-on, remove experimentalAgentDashboard toggle (#1538)
Co-authored-by: Orca <help@stably.ai>
2026-05-07 17:16:51 -07:00
Jinwoo HongandOrca 22b63a0191 Mobile: indefinite phone-fit hold + configurable auto-restore (#1532)
Co-authored-by: Orca <help@stably.ai>
2026-05-07 16:47:31 -07:00
45d05c3699 Add Kagi search support with encrypted private session links (#1434)
Co-authored-by: Orca <help@stably.ai>
Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com>
2026-05-07 00:40:38 -07:00
Neil 7b83b2dcdc fix: avoid repeated macOS privacy prompts (#1524)
* fix: avoid repeated macos privacy prompts

* fix: reduce background worktree permission probes

* chore: pin oxlint for ci

* fix: preserve optional rpc params with zod 4.4

* fix: preserve optional inline rpc params with zod 4.4
2026-05-06 23:30:03 -07:00
NeilandOrca 8d0b7b032a feat(pi): wire pi panes into the unified agent-hooks pipeline (#1501)
Co-authored-by: Orca <help@stably.ai>
2026-05-06 17:26:50 -07:00
9f8bf81c38 feat(source-control): commit, push, pull, and sync actions in panel (#1211)
Co-authored-by: Orca <help@stably.ai>
Co-authored-by: Alexander Saavedra <mralexsaavedra@gmail.com>
Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: brennanb2025 <brennankbenson@gmail.com>
2026-05-06 15:03:52 -07:00
Jinjing 5c7e5f2f1d fix: default workspaces grouping to projects (#1513) 2026-05-06 14:51:37 -07:00
Jinwoo HongandOrca 63e36d05fd chore(mobile): App Store prep — privacy manifest, debug-log cleanup, and protocol-version compat block (#1440)
Co-authored-by: Orca <help@stably.ai>
2026-05-05 19:23:56 -07:00
NeilandOrca 806ee58585 fix(pi): prefill linked issue URL via env var to bypass paste-readiness race (#1477)
When the new-workspace draft flow targets pi (e.g. user picks a GH issue
in the smart input + selects pi as the agent), the URL was supposed to
land in pi's input box as an editable draft. It didn't, because pi has
no `--prefill` flag and our bracketed-paste-after-ready path waits for
1.5s of stream silence after `\x1b[?2004h` — pi's startup banner
(Skills/Prompts/Extensions/conflicts) prints continuously for 5+ seconds,
which keeps the quiet-timer resetting until the 8s budget expires and
the paste is dropped. Claude works because of `--prefill`; codex works
because its startup is short enough to hit the quiet window.

Fix mirrors Claude's `--prefill` semantics for pi:

- Orca's pi overlay now installs a tiny `orca-prefill.ts` extension
  alongside the existing titlebar spinner. On `session_start` it reads
  `ORCA_PI_PREFILL` and calls `pi.ui.setEditorText(text)`, then deletes
  the env var so subsequent /new sessions don't re-prefill.
- `TuiAgentConfig` gains `draftPromptEnvVar`; pi sets it to
  `ORCA_PI_PREFILL`.
- `buildAgentDraftLaunchPlan` returns `{ launchCommand: 'pi', env: {...} }`
  for env-var agents; the env is plumbed through `startup.env` into
  `pty:spawn` (already supported by pty-connection.ts).

The launched shell command stays a clean `pi` — no `FOO='...' pi` prefix
typed into the terminal — and the readiness race is sidestepped entirely.

Co-authored-by: Orca <help@stably.ai>
2026-05-05 17:37:05 -07:00
NeilandOrca e49d90bee4 fix(gh-project): diagnose env-shadowed gh tokens in auth errors (#1478)
* fix(gh-project): diagnose env-shadowed gh tokens in auth errors

`gh auth refresh -s project` silently no-ops when GITHUB_TOKEN/GH_TOKEN
is exported in the user's shell — gh prefers env tokens and refuses to
modify them, exiting 0. Users follow the canned remediation, see no
error, retry, and stay stuck.

Add a one-shot `gh auth status` probe (gh:diagnoseAuth IPC) that:

- Detects env-shadowed credentials and rewrites the fix to `unset
  GITHUB_TOKEN` plus a grep to find where it's exported.
- Detects missing gh install, plain missing-scope on a keyring login,
  and SAML SSO authorization.
- Surfaces a tailored multi-button error UI in ProjectViewWrapper and
  ProjectPicker instead of one canned 'Copy command'.

Co-authored-by: Orca <help@stably.ai>

* fix(gh-project): address review feedback

- Cross-platform shell guidance: PowerShell commands on Windows
  (Get-ChildItem Env:, Remove-Item Env:, [Environment]::SetEnvironmentVariable)
  via navigator.userAgent platform check.
- Use `window.api.shell.openUrl` for the docs button instead of
  `window.open`, matching SidebarToolbar's external-URL pattern.
- Tighten gh auth status parser: accept single-label hostnames and
  optional trailing colon; recover host from the inline 'Logged in to
  <host>' line so a missed section header never silently drops accounts.
- Add tests for multi-host output and host-recovery fallback.
- Drop dead command/copy locals in ProjectViewWrapper.ErrorState by
  short-circuiting the auth-error case before they're computed.

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Orca <help@stably.ai>
2026-05-05 17:36:27 -07:00
Jinwoo HongandOrca 7f2f39b804 feat(mobile): pairing paste flow + Keychain-backed device tokens + deep link (#1475)
Co-authored-by: Orca <help@stably.ai>
2026-05-05 17:14:08 -07:00
Jinwoo Hong 4140d6a4c0 Revert "feat(mobile): pairing paste flow + Keychain-backed device tokens (#1452)" (#1474)
This reverts commit 55d3a42079.
2026-05-05 16:29:00 -07:00
Brennan BensonandOrca 8de43cd3b4 fix(sidebar): stamp lastActivityAt on first worktree discovery (#905)
Co-authored-by: Orca <help@stably.ai>
2026-05-05 16:17:51 -07:00
Jinwoo HongandOrca 55d3a42079 feat(mobile): pairing paste flow + Keychain-backed device tokens (#1452)
Co-authored-by: Orca <help@stably.ai>
2026-05-05 16:09:26 -07:00
JinjingandOrca ddb732cf34 Support spritesheet pet imports for Sidekick (#1472)
* WIP: Changes before auto-review fixes

Co-authored-by: Orca <help@stably.ai>

* auto-review-fix iteration 1

Co-authored-by: Orca <help@stably.ai>

* fix: plumb manifest.fps for detected-frame bundles

Co-authored-by: Orca <help@stably.ai>

* fix: address lint errors

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Orca <help@stably.ai>
2026-05-05 15:56:54 -07:00
Brennan BensonandOrca 148f33c470 fix(telemetry): refresh settings_changed whitelist; agent_kind runtime guard (#1450)
Co-authored-by: Orca <help@stably.ai>
2026-05-05 12:59:15 -07:00
Brennan BensonandOrca 6f2e31afad feat(telemetry): PR 4 — wire 7 core events to call sites (#1433)
Co-authored-by: Orca <help@stably.ai>
2026-05-05 11:57:51 -07:00
Jinwoo HongandOrca 705be9e312 feat(status-bar): consolidate memory + sessions into Resource Usage segment (#1415)
Co-authored-by: Orca <help@stably.ai>
2026-05-05 11:38:18 -07:00
JinjingandOrca 5ce9c3b481 feat(tasks): remember tasks page resume state across sessions (#1442)
Persist transient Tasks page position (GitHub mode, active preset/query,
Linear preset/query) in PersistedUIState so reopening Tasks restores the
user's working context instead of falling back to defaults. Source, repo
selection, team selection, and active project keep using their existing
settings paths.

Co-authored-by: Orca <help@stably.ai>
2026-05-05 11:19:50 -07:00
NeilandOrca 62e02a3b4c feat(new-workspace): unified source picker + non-submitted URL drafts for agents (#1426)
* Fix new workspace composer focus restore

* Unify new workspace source selection

* WIP: selected source pill in smart workspace name field

Co-authored-by: Orca <help@stably.ai>

* fix(new-workspace): truncate source pill so it doesn't expand the dialog

Co-authored-by: Orca <help@stably.ai>

* feat(new-workspace): add open-in-browser button to source pill, fix vertical alignment

Co-authored-by: Orca <help@stably.ai>

* refactor(new-workspace): drop redundant kind suffix, distinct PR/issue icons, tooltips on pill actions

Co-authored-by: Orca <help@stably.ai>

* fix(new-workspace): type linked URL into agent input without auto-submit

Co-authored-by: Orca <help@stably.ai>

* fix(new-workspace): use bracketed-paste for draft URL injection so it actually appears in the agent input

Co-authored-by: Orca <help@stably.ai>

* feat(agents): per-agent draft injection strategy (codex slow paste, pi/opencode type-chars)

Co-authored-by: Orca <help@stably.ai>

* fix(agents): smarter TUI-ready heuristic + bracketed paste for codex/pi/opencode

Replaces per-agent strategy guesswork with a measured readiness check:
title-idle / non-shell-foreground stable for 1.5s / 2.5s minimum floor.
Verified against codex, pi, opencode, claude in a node-pty + xterm-headless
test rig — bracketed paste lands in the input buffer for all four.

Co-authored-by: Orca <help@stably.ai>

* refactor(agents): drop unused per-agent draft strategy abstraction

The TUI-ready heuristic in agent-paste-draft.ts works for every tested
agent (claude/codex/pi/opencode), so the AgentDraftInjectionStrategy
field, type-chars + bracketed-paste-slow code paths, and per-agent
overrides are dead. Keep the `agent` arg on pasteDraftWhenAgentReady
for future per-agent escape hatches without touching every call site.

Co-authored-by: Orca <help@stably.ai>

* feat(agents): skip draft URL injection for copilot + cursor-agent

Both TUIs open with a 'Do you trust this folder?' menu on first launch
that consumes keystrokes as menu input — pasting a URL there either
selects an arbitrary option or quits the session. Mark them with
skipDraftUrlInjection so the workspace still opens cleanly; the user
types/pastes the URL themselves once past the trust menu.

Co-authored-by: Orca <help@stably.ai>

* feat(agents): native --prefill for claude, trust pre-write for cursor/copilot

Replaces the empirical TUI-ready waits with two deterministic mechanisms:

1) `claude --prefill <text>` flag — Claude launches with the URL already in
   its input box, no submit. Eliminates the readiness/paste race entirely
   for the most common agent.
2) DECSET 2004 (`\x1b[?2004h`) detection on the PTY data stream for every
   other agent. That escape is the protocol-level "input layer ready,
   accepting bracketed paste" handshake — emitted by claude/codex/pi/
   opencode/gemini/cursor-agent/copilot the moment the input box mounts.
   We tap it via a sidecar subscription on pty-dispatcher (no interference
   with the primary xterm handler) and paste as soon as it lands. The
   8s budget is now an upper bound, not a target.

Cursor-agent and Copilot's "Do you trust this folder?" menus are bypassed
by writing the same trust artifacts the CLIs themselves write after the
user accepts:
- Cursor: `~/.cursor/projects/<slug>/.workspace-trusted` (slug = abs path
  with leading `/` stripped, remaining `/` → `-`).
- Copilot: append cwd to `trustedFolders` in `~/.copilot/config.json`
  (the same array the bundled `addTrustedFolder` writes).

Verified against the cursor-agent CLI bundle (versions/2026.04.17-787b533/
index.js: `_=".workspace-trusted"`) and the @github/copilot 1.0.32 bundle
(`isFolderTrusted` / `addTrustedFolder` both read/write `trustedFolders`).
Both check via realpath() before string-comparing, so the trust preset
canonicalizes too.

skipDraftUrlInjection is dropped — both agents now get the draft URL
paste once the trust menu is pre-resolved.

Tests: 24 passing across tui-agent-startup, agent-trust-presets,
pty-dispatcher routing.

Co-authored-by: Orca <help@stably.ai>

* fix(agents): wait for post-?2004h render burst to settle before paste

OpenCode emits DECSET 2004 at ~500ms during alt-screen setup, then runs
a 1.3s splash render with NO bytes on the PTY, then paints the actual
input box at ~1.85s. Pasting on the bare ?2004h signal lands during the
silent gap and the bytes are dropped.

The fix: take ?2004h as the necessary precondition, then wait for the
TUI's render burst to finish — defined as 1500ms of stream silence
after the most recent post-?2004h byte. This captures both the fast
TUIs (claude/pi/codex emit setup escapes in one burst then go quiet)
and the slow ones (opencode emits, sleeps for the splash, emits again,
then goes quiet).

Verified against opencode/claude/pi in a node-pty rig: paste lands on
the first try with the new strategy. The hard 8s timeout still caps
the wait when an agent fails to launch.

Co-authored-by: Orca <help@stably.ai>

* fix(agents): guard agentTrust IPC so stale preload doesn't crash launch

If the preload bundle is older than the renderer (a real situation in
electron-vite dev because preload changes only apply on full restart,
not HMR), `window.api.agentTrust` is undefined and the launch crashes
with "Cannot read properties of undefined (reading 'markTrusted')"
before the worktree even opens.

Guard the call sites in launch-work-item-direct and useComposerState
to skip the trust pre-write when the IPC isn't exposed, and wrap the
invoke in try/catch so an IPC error never blocks the launch — the user
just sees the trust menu and accepts it manually, same as before this
feature shipped.

Co-authored-by: Orca <help@stably.ai>

* feat(tasks): route 'Use' through the New Workspace dialog instead of yolo-create

The Use CTA on the Tasks page used to create+activate a worktree
synchronously, which surprised users — the worktree appeared in the
sidebar before they had a chance to confirm name / agent / setup. The
unified New Workspace dialog landed in this branch already supports
opening with a linked work item pre-filled (see openComposerForItem /
openComposerForLinearItem), so just route Use through it.

The launchWorkItemDirect helper stays exported for ProjectViewWrapper,
which has its own UX where the immediate-create flow is the right call.

Co-authored-by: Orca <help@stably.ai>

* test(agents): include `agent` field in autohand startup-plan assertion

Merging main brought in the Autohand Code agent test (PR #1382), which
predated this branch's addition of `agent` to AgentStartupPlan.
Aligning the assertion fixes the lone CI test failure on this PR.

Co-authored-by: Orca <help@stably.ai>

* refactor(agents): drop unused expectedProcess arg + snapshot sidecar set

Two minor follow-ups from self-review:

1. `pasteDraftWhenAgentReady` no longer reads `expectedProcess` — readiness
   is gated on DECSET 2004 alone now, not on PTY foreground process. Drop
   it from the signature and from the two callers (launch-work-item-direct,
   new-workspace).

2. The pty-dispatcher's sidecar fan-out iterates the live Set, which is
   safe against deleting the current element but not against a watcher
   that synchronously subscribes a sibling. Snapshot via Array.from
   before the loop. Cheap (Set is tiny) and removes the latent footgun.

No behavior change.

Co-authored-by: Orca <help@stably.ai>

* test(e2e): match the unified smart-name input's new placeholder

The CreateFromTab refactor in this branch replaced the separate "Workspace
name" Input with a single SmartWorkspaceNameField whose default-mode
placeholder is "Type a name, #1234, branch, GitHub or Linear URL". The
worktree-create e2e test was still anchoring on the old "Workspace name"
text and could not find the input.

Update the placeholder regex to match the new copy. Free-form text typed
into smart mode is treated as a workspace name by submitQuick — same
contract the test used before.

Verified locally: targeted e2e passes in 2.2s.

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Orca <help@stably.ai>
2026-05-05 01:07:52 -07:00
f9120cb2f1 Add custom desktop notification sounds (#1430)
* Add support for custom desktop notification sounds

* perf(notifications): cache custom sound + restart-on-play

Avoids re-reading the configured audio file (up to 10MB) from disk and
re-transferring it over IPC on every notification. Adds a path-only
resolver so repeated dispatches with an unchanged sound skip the heavy
load entirely.

For burst handling, follows the VS Code AccessibilitySignalService /
GNOME canberra pattern: one shared HTMLAudioElement per sound, restarted
from t=0 on each play, with an in-flight guard that drops new plays
while the sound is still ringing. This self-dedupes by the sound's own
duration without any magic time constant — distinct sounds remain free
to overlap. The Test button passes force: true so an explicit user
action always plays through.

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>
2026-05-05 01:00:16 -07:00
Kaylee 4497b5fa3d feat: custom IDE Font (#1428)
* Add an Appearance setting for the app/workbench UI font family. It will be independent from terminal typography and will not change Monaco, diffs, markdown editor text, or terminal panes.

* Update IDE font copy
2026-05-05 00:29:41 -07:00
Igor Costa 38b77afb8d Add Autohand Code agent support (#1382) 2026-05-05 00:25:37 -07:00
e623372cdb feat(cli): add tab profile controls and automation primitives (#1396)
* feat(cli): add browser tab profile controls

* feat(cli): add tab profile automation primitives

* refactor(cli): narrow tab profile automation scope

* chore: retrigger PR checks

* review: harden tab profile automation CLI

- Wait for tab re-registration after browser.tabSetProfile so a follow-up tab list --show-profile reads the new sessionProfileId from BrowserManager instead of the stale one from the previous webview
- Wait for tab registration after browser.tabProfileClone, matching browser.tabCreate, so the cloned browserPageId is operable when the CLI returns
- Short-circuit browser.tabSetProfile when the tab is already on the requested profile so we do not tear down and remount the webview for a no-op switch
- Switch TabShow.worktree from OptionalPlainString to OptionalString to match every other tab schema; empty --worktree should fall back to the active worktree, not pass through as the empty string
- Add max-lines disable to browser.test.ts (file grew past 300 lines after adding the new tab-profile and tab-show tests)

* review: fix useIpcEvents test setup for tab profile API

CI failure: useIpcEvents.test.ts threw at module load with TypeError: window.addEventListener is not a function. The chain: the rebased useIpcEvents.ts imports destroyPersistentWebview from webview-registry, which calls window.addEventListener at module load. The test stubs window via vi.stubGlobal as a plain object without addEventListener, so the typeof window check passes but the call throws.

- webview-registry.ts: tighten the module-load guard to also check that window.addEventListener is callable, so importing this module from a non-DOM-ish test env (vitest node env with stubbed window) does not throw at module load
- useIpcEvents.test.ts: add the new onRequestTabSetProfile and replyTabSetProfile stubs to all 8 window.api.ui mocks so the new IPC subscription registered by useIpcEvents resolves

* review: restore profile CRUD lost during rebase onto 1397-merged main

The rebase brought commit 3242aa27 (refactor: narrow tab profile automation scope) onto a main that already had the lifecycle CRUD from 1397. The refactor commit removes BrowserProfileList/Create/Delete types, runtime methods, RPC registrations and schemas, plus the help/specs entries, because those were the precursor versions in commit 1 of this branch. Post-rebase those removals land on the hardened versions inherited from main, breaking 1397.

Restore:
- runtime-types.ts: BrowserSessionProfile import; ProfileList/Create/Delete result types
- orca-runtime.ts: ProfileList/Create/Delete result type imports; browserProfileList/Create/Delete methods
- browser-core.ts: ProfileCreate, ProfileDelete schema imports; browser.profileList/profileCreate/profileDelete RPC registrations
- browser-schemas.ts: ProfileCreate, ProfileDelete zod schemas
- help.ts: list/create/delete subcommand lines under Browser Automation
- specs/browser-basic.ts: list/create/delete spec entries

---------

Co-authored-by: Nikolatesla-lj <Nikolatesla-lj@users.noreply.github.com>
Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com>
2026-05-04 23:46:23 -07:00
JinjingandOrca 70ffa0a73d [feat] Add a full Github project view under "Tasks" (#1424)
* WIP: auto-review-fix iteration 1 (project-view.ts fixes applied)

Co-authored-by: Orca <help@stably.ai>

* refactor(github-project): split project-view and slug-dialog into modules

Co-authored-by: Orca <help@stably.ai>

* fix(lint): resolve oxlint errors in project-view modules

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Orca <help@stably.ai>
2026-05-04 23:24:52 -07:00
9bf339eeb9 feat(cli): add tab profile lifecycle commands (#1397)
Co-authored-by: Orca <help@stably.ai>
Co-authored-by: Nikolatesla-lj <Nikolatesla-lj@users.noreply.github.com>
Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com>
2026-05-04 23:01:15 -07:00
Brennan BensonandOrca 26a51a1d56 feat(telemetry): PR 3 — first-launch UX + Privacy pane; flip transport flag (#1385)
Co-authored-by: Orca <help@stably.ai>
2026-05-04 20:46:21 -07:00
JinjingandOrca 56686ea035 fix(worktrees): preserve SSH focus recency and route agent clicks through activation helper (#1410)
- Scope pruneLastVisitedTimestamps per-repo so not-yet-hydrated SSH repos retain persisted Cmd+J recency instead of being wiped at startup.
- Parse lastVisitedAtByWorktreeId leniently: drop only bad entries rather than failing the whole workspace session on one corrupted timestamp.
- Route sidebar agent-tab clicks through activateAndRevealWorktree so cross-repo activation and nav history are not silently skipped.

Co-authored-by: Orca <help@stably.ai>
2026-05-04 14:58:09 -07:00
82023dbe1b feat(worktrees): add experimental symlinks for shared paths on worktree creation (#1029)
Co-authored-by: Orca <help@stably.ai>
Co-authored-by: federico-ntb <federico@notchatbot.com>
Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com>
2026-05-04 14:19:20 -07:00
Jinwoo HongandOrca fc578f5ea9 feat(mobile): Expo companion app [beta] (#1245)
Co-authored-by: Orca <help@stably.ai>
2026-05-04 13:08:27 -07:00
JinjingandOrca fd6abb5808 fix(cmd-j): rank empty-query worktrees by focus recency (#1383)
* fix(cmd-j): rank empty-query worktrees by focus recency

Persist a per-worktree focus-recency timestamp and use it as the primary
ordering signal for Cmd+J's empty-query Worktrees section, so SSH and
other quiet worktrees surface based on user focus rather than background
activity. See docs/cmd-j-empty-query-ordering.md.

Co-authored-by: Orca <help@stably.ai>

* fix(tests): guard lastVisitedAtByWorktreeId and mock markWorktreeVisited

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Orca <help@stably.ai>
2026-05-03 22:55:11 -07:00
Brennan BensonandOrca fd86e1869a feat(telemetry): PR 2 — transport (client, validator, burst cap, IPC, build gate) (#1374)
Co-authored-by: Orca <help@stably.ai>
2026-05-03 16:51:31 -07:00
7edcdca699 feat: hide default branch workspace (#1249)
Co-authored-by: Orca <help@stably.ai>
Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com>
2026-05-03 15:55:43 -07:00
Brennan BensonandOrca d0e3a64768 feat(telemetry): PR 1 — foundations (types, consent, migration, PRIVACY.md) (#1372)
Co-authored-by: Orca <help@stably.ai>
2026-05-03 13:13:26 -07:00
JinjingandOrca 46a26252bb feat(browser): viewport-size emulation via CDP (#1369)
* feat(browser): viewport-size emulation via CDP

Adds a Viewport Size submenu in the browser toolbar "…" menu.
Presets apply width/height/deviceScaleFactor/mobile/touch via
Emulation.setDeviceMetricsOverride + setTouchEmulationEnabled, and
swap the UA to a mobile iPhone CriOS string on mobile presets.
Responsive clears the override. Selection persists per-tab and
re-applies on dom-ready so it survives navigations.

Co-authored-by: Orca <help@stably.ai>

* chore(browser): rename 'Responsive' viewport option to 'Default'

Co-authored-by: Orca <help@stably.ai>

* fix(browser): harden viewport emulation — serialize, validate, client-hints

- Chain per-tab setViewportOverride calls to prevent rapid-toggle races
- Validate viewport metrics at IPC trust boundary (reject non-finite/out-of-range)
- Emit userAgentMetadata alongside mobile UA to avoid UA/CH mismatch
- Always reapply on dom-ready (incl. null) to clear stale emulation
- Persist viewportPresetId in session schema (optional+nullable for back-compat)
- Convert preset submenu to DropdownMenuRadioGroup for a11y
- Log debugger.attach failures and cover with a unit test

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Orca <help@stably.ai>
2026-05-02 20:22:40 -07:00