mirror of
https://github.com/stablyai/orca.git
synced 2026-10-21 00:03:46 +00:00
ec0dbbf6794daf7440d00a902fadd441906abfa3
448
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
cd05f2ff93 | Implement robust orchestration primitives and connected-server workers (#9925) | ||
|
|
97e4776dfe |
feat(plugins): Orca plugin system — kernel, content packs, panels, workers, marketplace v0 (experimental) (#8549)
* feat(plugins): Orca plugin system — kernel, content packs, panels, workers, marketplace v0 (experimental) Adds Orca's experimental plugin system behind a settings flag: a supervised kernel, declarative content packs (VM recipes, commands and keybindings, language packs), sandboxed iframe panels, forked worker hosts, and a Git-backed marketplace v0 with consent, provenance and kill-list enforcement. Theme, icon-theme and terminal-theme contributions are deferred to a follow-up pass. * fix(plugins): make unsupported marketplace listings unreachable by key findPlugin() backs preview/install/previewInstalledUpdate via requireListing(), so filtering only listPlugins() hid the catalog card while leaving the dead install path reachable one click later. * fix(plugins): fan Pi session-only status out to plugin subscribers The providerSessionOnly early-return in applyNormalizedStatus emitted to onAgentStatus (main-window fanout) but skipped enrichedStatusListeners, so plugins subscribed to agent.status.changed silently missed every Pi session_start event. Route both emit sites through one helper so a future early return cannot drop the plugin tap again. Co-authored-by: Orca <help@stably.ai> * plugins: drop dead code and hoist duplicated trust-boundary patterns Cleanup pass over the P1 diff, no behavior change: - Delete `readPluginTreeSnapshot`/`readSnapshotFile` and their types, plus the now-vestigial `directories`/`signal` plumbing in `collectFiles`. - Delete `resolveContainedPluginDirectory` (no callers). - Delete `plugin-content-load-pool.ts`; it reimplemented the existing `mapWithConcurrency`, whose index arg also removes the pairing wrapper in `buildPluginList`. - Hoist `PLUGIN_CONTENT_HASH_PATTERN` and `PLUGIN_COMMIT_PATTERN` into the install-lockfile module; 11 sites hand-rolled these identically. - Point the new reliability gate at the PR instead of gitignored docs paths, matching every other gate's link form. * fix(plugins): retry plugin state renames on Windows AV/EPERM locks Six plugin write paths (lockfile, provenance, current pointer, kill list, marketplace cache, staged install dir) did a plain rename, so an antivirus or indexer holding the target open surfaced as a failed install. The repo already retries this hazard for issue #1507, but only through a sync helper; these paths are all async. Adds one bounded async retry + atomic write used by all six, and trims a consent-provenance header that restated its own JSX. * test(plugins): cover the Windows rename retry path The retry loop shipped untested: both existing cases hit the non-retry path, and the temp-cleanup test passed identically with the `finally` removed. Mock `rename` to queue errno codes so CI can exercise locks it cannot provoke. Co-authored-by: Orca <help@stably.ai> * fix(plugins): pin bundled plugin resources to LF Windows CI checks out with autocrlf, so the byte-hashed launch tree arrived as CRLF and verify-packaged-plugin-resources rejected it — the packaged build could never pass on Windows. Reproduced locally: CRLF yields the exact CI error, LF verifies clean. Files are already LF, so nothing renormalizes. Co-authored-by: Orca <help@stably.ai> * test: guard the bundled-plugin LF pin against a CRLF checkout The byte-hash mismatch only surfaced in Windows packaging CI. Assert the .gitattributes pin and that a CRLF tree is rejected, so a regression fails on any platform instead of waiting for a packaged Windows build. Co-authored-by: Orca <help@stably.ai> * ci: trigger packaged-build check on bundled plugin resource changes The launch tree is byte-hashed during packaging, but no trigger path covered it — so the CRLF fix for that check would not have re-run the check. Add the resources, verifier and .gitattributes paths that can break packaging. Co-authored-by: Orca <help@stably.ai> * perf(plugins): rebuild the panel frame only when its baked theme values change The revision keys the panel iframe, so every bump destroys the sandboxed frame and its in-panel state. It counted root attribute mutations, but --workspace-sidebar-live-width is written every rAF of a sidebar drag, so dragging with a panel open blanked it ~60x/sec. Compare the two values the shell actually bakes in instead. Co-authored-by: Orca <help@stably.ai> * test: stop pinning a plugin name in the CRLF guard The CRLF case rewrites every launch file, so the reported mismatch is whichever plugin sorts first. P2 adds theme plugins that sort ahead of orca-navigation-shortcuts, which broke the assertion there. Co-authored-by: Orca <help@stably.ai> * style: drop stray blank lines left by the rebase resolutions Both sides of the agent-hooks and orca-runtime conflicts contributed a trailing blank, which oxfmt rejects. Whitespace only. Co-authored-by: Orca <help@stably.ai> * test(plugins): stop the startup budget failing on machine load P95 runs 16-34ms idle but exceeds the 50ms bound under full-suite parallelism, so the gate flaked. Widen it to catch an order-of-magnitude regression instead; the no-worker/no-plugin-code assertions are the real guarantee. Verified a 400ms regression still fails. Co-authored-by: Orca <help@stably.ai> --------- Co-authored-by: Orca <help@stably.ai> |
||
|
|
28b395ced2 |
fix(mobile): harden native chat send budgets, streams, and stop (#10814)
Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com> |
||
|
|
28dfc13654 | feat(sidebar): distinguish and filter CLI-created workspaces (#10712) | ||
|
|
33bd676644 |
fix(github): align PR source and review head origin (#10677)
* fix(github): align PR source and review head origin * fix(github): pin number-based work item open to the repo source preference Open-by-number and details still ran the upstream-first multi-candidate PR probe, so a fork and its upstream sharing a PR number opened different PRs than the list and start-point paths did once #10677 pinned those to origin. Thread repo.issueSourcePreference through dispatchWorkItem, getWorkItemDetails, getRepoWorkItem, and getRepoWorkItemDetails. getWorkItemByOwnerRepo is left alone: explicit owner/repo already pins identity. auto/upstream/undefined keep the multi-candidate probe. Co-authored-by: Orca <help@stably.ai> * test(github): enforce origin preference in review head origin resolution The explicit origin preference must short-circuit before any identity probe, so no remote queries should occur. Add validation to reject unexpected remotes and tighten the test assertion to verify no remote get-url calls happen at all. * fix(github): enforce origin preference in issue open-by-number lookup listWorkItems and getWorkItem must share preference so origin/upstream toggles cannot disagree. Explicit origin preference now fail-closes when origin identity is unresolved (no bare-lookup fallback), matching the PR candidate resolution rule. --------- Co-authored-by: Orca <help@stably.ai> |
||
|
|
8e785dd0bc | fix(daemon): preserve completion inspection for legacy protocols (#10679) | ||
|
|
ca70be8318 |
Add {linkedIssue} template variable for commit and PR generation (#10640)
* feat(source-control-ai): add {linkedIssue} recipe variable for commit and PR prompts
Custom commit-message and pull-request recipes can now reference the GitHub
issue linked to the workspace, so a template like "Fixes #{linkedIssue}" lands
the closing trailer without the user retyping the number.
- register `linkedIssue` on the commitMessage and pullRequest actions only,
with the VARIABLE_INFO entry the chip hover card requires
- substitute unconditionally via `formatLinkedIssueTemplateValue` (empty string
when nothing resolves) so the token never survives into a prompt; enrich the
draft context conditionally via `withLinkedIssueDraftContext` so unlinked
workspaces keep their existing context shape
- attach at the 7 call boundaries (runtime commit x2, runtime PR shared, IPC
commit x2, IPC PR x2); the pure git gather stays pure
- validate the renderer-supplied worktreeId against the request path and repoId
before any meta read, comparing SSH paths as raw strings so a Windows host
cannot rewrite a remote POSIX path
- built-in prompts are unchanged; no GitLab dual-read and no default trailer
* fix(source-control-ai): resolve {linkedIssue} adversarial review findings
Addresses 13 of the 14 findings from the {linkedIssue} code review
(6 minor, 8 nit, 0 critical, 0 major); Issue 5 (GitLab provider naming)
is deferred to design Open Question 3 as product expansion.
Behavior:
- Dialog previews the workspace's real linked issue instead of the
synthetic 123, in both the chip hover card and the plan preview, so an
unlinked workspace previews the `Fixes #` it will actually generate.
Settings dry-runs stay fully synthetic.
- Reject non-positive, fractional and unsafe-integer issue numbers at the
IPC resolver via a shared isLinkedIssueNumber predicate, so corrupt meta
never reaches a draft context (previously -7 rendered `Fixes #-7` and
1e21 rendered `Fixes #1e+21`).
- Fail closed on an empty-string repoId instead of skipping the cross-check.
Structure:
- Split the variable registry into source-control-ai-action-variables.ts
and re-export it, restoring max-lines headroom with no consumer churn
and no lint disable.
- Constrain withLinkedIssueDraftContext to contexts declaring linkedIssue.
- Move the misplaced shared imports into their import group.
Docs and tests:
- Document that the IPC id/path validator guards relay/CLI/future callers,
not the renderer (whose path is id-derived), and rename the three tests
that read as proof of a protection that cannot fire.
- Add PR-side coverage that was missing: three git:generatePullRequestFields
handler tests, a built-in PR prompt no-leak guard, and the runtime PR
unlinked case.
- Replace the coincidental '42' assertion with a fixture-unique sentinel.
- Type the runtime worktree fixture with satisfies, which surfaced and
fixed pre-existing drift in its git sub-object.
- Add an e2e case covering the preload -> main -> meta -> template chain.
Co-authored-by: Orca <help@stably.ai>
* fix(source-control-ai): resolve {linkedIssue} adversarial re-review findings
Addresses all 8 findings from the {linkedIssue} code re-review
(2 minor, 6 nit, 0 critical, 0 major); none deferred.
Behavior:
- Revert the variableOverrides parameter on planSourceControlTextGeneration.
Its result is a Save/Generate gate, not a preview, and the recipe it
validates is saved repo- or globally scoped -- so rendering it against the
active workspace disabled both buttons with "Command input is empty." for a
{linkedIssue}-only template on any unlinked workspace, blocking a global
settings write. Validation is synthetic again; chip previews are unchanged.
- Make the chip hover card additive instead of either/or. A supplied preview
now appends a "This workspace" sample below the description and Example
rather than replacing them, so the GitLab-empty and dangling `Fixes #`
warning survives on the two dialogs where recipes are actually authored.
basePrompt keeps its preview-only shape, where the preview is the content.
Structure:
- Drop the registry re-export from source-control-ai-actions.ts and move the
last two consumers onto source-control-ai-action-variables, so one import
path per symbol keeps a grep of the registry's consumers complete.
- Split the registry/helper suites into source-control-ai-action-variables.test.ts
so each test file mirrors its module.
Tests:
- Cover the Save/Generate gate at the canRunGeneration level for a bare
{linkedIssue} recipe on linked and unlinked workspaces, with a negative
control proving the buttons can still be disabled.
- Cover the chip hover card directly; the dialog tests mock it away.
- Guard the PR mismatched-id test with toHaveLength(1) so it cannot pass
vacuously on an unrelated early return.
- Add an unlinked-workspace e2e case (saw-issue:empty), which is what
distinguishes a real resolver from one that always returns a number.
Spec now runs green: 3 passed.
- Rename the dialog test that claimed a synthetic-fallback assertion it did
not make, and route its renders through one shared helper.
Docs are worktree-local (.gitignore:84 ignores docs/**): the design doc's
plan-preview and chip-surface claims, the manual QA rows, and both reviews'
statements about pre-existing PR-handler tests are corrected there.
* fix(source-control-ai): make the {linkedIssue} e2e guard and dialog test falsifiable
The e2e unlinked case extracted the echoed issue with `ORCA_E2E_ISSUE=(\d*)`,
which matches zero digits in front of an unexpanded `{linkedIssue}` and reported
it as `empty` — so the case that exists to catch a literal token surviving into
a prompt passed on exactly that regression. Capture the whole line instead: a
literal now arrives as `saw-issue:{linkedIssue}` and fails, verified by dropping
the substitution key for unlinked contexts and watching the case go red.
Also drop the inert `not.toContain('Command input is empty.')` assertion — that
copy is click-driven `generationError` state and this suite renders statically,
so it could never fail; the claim it reached for is carried by the plan test.
Rename two plan tests off the "plan preview" framing the design now rejects.
Local review artifacts (design doc, implementation notes, final review) were
swept to match the tree in the same pass; they are gitignored here.
* Resolve {linkedIssue} from live metadata, not cache
Resolved worktrees are cached for a second, causing commit and PR
generation to use stale linked-issue state. Hosts now implement
getWorktreeLinkedIssue to provide fresh issue metadata by worktree id,
with proper fallback for unlinked workspaces. Updates both commit
message and PR field generation paths; includes integration and e2e
coverage.
* Keep cached linkedIssue when metadata is unavailable
Return undefined from getWorktreeLinkedIssue when live metadata cannot be read
(store not ready), distinguishing it from null (unlinked). The caller now falls
back to the cached worktree value instead of treating unavailable as unlinked.
Also extract the linked-issue echo generator as a shared e2e test helper.
---------
Co-authored-by: Orca <help@stably.ai>
|
||
|
|
b31e9bb03d |
fix(worktree): bound the .worktreeinclude copy so a huge include can't freeze workspace creation (#10540)
* fix(worktree): bound the .worktreeinclude copy so a huge include can't freeze creation `.worktreeinclude` copying was bounded in entry count (1000) but unbounded in bytes and files, and awaited inline during worktree creation. A repo listing `node_modules` froze creation for minutes behind the create dialog on Linux and Windows, where the fallback is a full `fs.cp` (macOS gets a cheap APFS clone). Measure each copy-mode source against a cumulative budget (2 GB / 50k files) before the first byte is written, and refuse the entries that bust it. Refused entries ride the existing `CreateWorktreeResult.warning` channel so a workspace never silently comes up missing its included files. Pre-measurement rather than mid-copy abort: `fs.cp` ignores its `signal` option, so a started copy cannot be cancelled and would strand a partial tree. Refusing up front means there is no partial state to clean up. * fix(worktree): don't charge bytes for copy-on-write clones, and bound the sizing walk Two defects in the copy budget, both found by review: - The byte limit was applied on macOS, where the copy is an APFS clonefile. Measured: a 2.7 GB tree clones in 22 ms and consumes no disk. Refusing it on a 2 GB byte ceiling denied work that was already free — a regression on the one platform this bound was never meant to touch. Bytes are now charged only when a byte-for-byte copy will actually run; the volume probe that decides this is the same cached df+diskutil pair the clone runs, and writes nothing, so the "refuse before the first byte" invariant holds. The entry limit still applies everywhere: inodes are real work even on the clone path. - A refused entry consumed no budget, so a `.worktreeinclude` listing many over-budget directories paid a fresh full-limit walk for each one — up to 1000 x 50,000 lstat calls, re-creating the stall this bounds. The walk is now charged against its own ceiling whatever the verdict. Also documents that `admit()` must be awaited sequentially (CodeRabbit). * fix(worktree): give the sizing walk headroom so one huge entry can't starve the rest The walk ceiling added in the previous commit was seeded with maxEntries, the same number the entry limit uses. Sizing an entry that busts the file-count limit walks maxEntries + 1, driving the ceiling negative, so every later `.worktreeinclude` entry was refused without being measured at all. That regressed the common case: a repo listing `node_modules` plus `.env` used to get `.env`; it silently got nothing. Reproduced, and now covered by a test that fails when the headroom is removed. The walk now gets 5x the entry budget, so total sizing work stays bounded (<=250k lstat per materialization, vs the 1000 x 50k this ceiling exists to prevent) while ordinary lists never reach it. Entries refused because earlier ones exhausted the walk report a distinct 'sizing' reason, so the warning stops quoting size limits at a 4-byte file that was never measured. * fix(worktree): bill a failed clone's bytes, and blame the right ceiling Two follow-on defects from the copy-on-write fix: - A predicted APFS clone that then failed mid-copy (EPERM, ENOSPC) fell through to a real `fs.cp` whose bytes were never charged, because the entry had been admitted on the premise that cloning is free. That reopened the unbounded copy on macOS. The measured size is already known, so the fallback now bills it and refuses if it no longer fits, reporting the entry as skipped instead of silently copying gigabytes. A clone that was never viable (ApfsCloneUnavailableError) was already charged as a real copy, so that path keeps falling back as before. - The walk ceiling is also applied inside the measurement via min(remainingEntries, remainingWalk), and when the walk term bound, the refusal was still reported as 'entries' — telling the user a 3-file directory busted a 4-file limit. It now attributes to whichever ceiling actually bound. Also fixes the singular warning text, which said "entry X was not copied ... copying them would exceed ... Copy them in manually". * fix(worktree): flag a partial clone leftover, cap the warning, cover two branches - A clone that fails partway only removes an *empty* reservation, so leftovers can survive at the target. Reporting that entry as simply "not copied" sent the user to copy it in manually, straight into a half-populated directory. Those skips now carry mayBePartial and the warning says to check the path first. Cleaning up the leftovers stays the deferred follow-up it already was. - The warning enumerated every skipped path. `.worktreeinclude` allows 1000 entries and all of them can be skipped, so it now names five and counts the rest — an unbounded string is a poor look in a PR about bounds. - Two load-bearing branches had no test, both proven by surviving mutants: the `bytesAreCopied` short-circuit (reachable when a wedged df/diskutil makes the volume probe answer "no clone", so bytes are charged up front and must not be billed twice), and chargeBytes actually consuming budget for later entries. * fix(worktree): only flag directory clones as partial, and cap that list too - mayBePartial was set for every refused clone fallback, but only a *directory* clone can leave anything behind: the file path clones into a temp name and publishes with link(2), so a failure leaves nothing at the target. Sending the user to inspect a path that does not exist is its own small lie. - The partial-copy sentence sliced to five names without the "and N more" that the other sentence appends, so entries past the fifth were surfaced nowhere. Both sentences now share one nameList helper. |
||
|
|
b600e25fa1 |
feat(worktrees): support project-level .worktreeinclude (literal paths) for copying gitignored files into worktrees (#9791)
* feat(worktrees): copy project-level .worktreeinclude paths into new worktrees Read .worktreeinclude at the repo root (gitignore syntax) and copy matching gitignored paths from the primary checkout into each newly created local worktree, so .env and other local config carry over with zero per-user setup. - Literal patterns resolve by direct stat; globs match against ls-files --others --ignored --exclude-standard --directory (collapsed dirs keep huge repos fast); every candidate is re-verified with check-ignore so tracked or unignored files are never copied. - Copy semantics, never symlink: APFS clone-copy on macOS, real copy elsewhere, so each worktree owns its files (unlike repo.symlinkPaths, which it merges with rather than replaces). - Failures never block worktree creation. - Remote (SSH) creation skips it, same as symlinkPaths. - Split APFS clone helpers into worktree-apfs-clone.ts (max-lines). Closes #7549 * fix(worktrees): harden worktree include copying * fix(worktrees): support nested includes on Git 2.25 * fix(worktrees): bound include copy costs * fix(worktrees): close include correctness and perf gaps * fix(worktrees): preserve included copy semantics * fix(worktrees): harden include resolution * fix(worktrees): preserve bounded include resolution * fix(worktrees): bound include filesystem resolution * fix(worktrees): harden include matching * fix(worktrees): tighten include matching and scan bounds * fix(types): use concrete filesystem stat types * fix(worktrees): harden included path materialization * perf(worktrees): stop include parsing at resolver budgets * chore(skills): refresh bundled skill manifests * refactor(worktrees): reduce .worktreeinclude to focused literal-only scope The reviewed implementation grew well past the ticket (#7549), which asks for a size-M feature that reuses existing worktree machinery. Trim back to the minimal change that solves the reported problem safely: - Resolver now supports literal files and directories only. Glob/negation lines are skipped with a warning (documented follow-up), which removes the entire user-controlled-regex ReDoS surface, the CPU/byte budgets, the git enumeration scan, and the case-sensitivity engine. The filesystem + git check-ignore handle existence and case for free. - Copy layer folded back into worktree-symlinks.ts (link/copy modes share one loop); dropped worktree-path-copy.ts, worktree-target-safety.ts, the descendant-dedup/realpath/target-parent machinery, and the per-materialization APFS filesystem cache. Kept the df/diskutil probe timeout. - Reverted unrelated changes: check-ignored-paths timeout param and the git-binary-compatibility enumeration tests. Net: -1903/+172 across the include+copy code. Behavior for the ticket's cases (.env, .env.local, .vscode/, node_modules, config/secrets.json) is unchanged; gitignored-only + copy-not-symlink semantics preserved. Closes #7549 * fix(worktrees): dereference symlinked .worktreeinclude entries + cache APFS volume probe Two issues found by review + perf audit of the copy path: - Correctness (HIGH): a listed entry that is itself a gitignored symlink was copied AS a symlink (fs.cp dereference:false), and the darwin APFS branch was skipped for all symlink sources. Editing the worktree's copy then wrote through to the shared/primary target — inverting copy-mode's 'each worktree owns its files' guarantee, and escaping the worktree entirely if the link pointed outside it. Now resolve realpath for a top-level symlink in copy mode so we copy content; nested symlinks inside a copied dir stay as-is (cp -R semantics). - Perf: assertSameApfsVolume ran df+diskutil per copied path (4 subprocesses each), so an N-entry include spawned ~4N short-lived processes on the macOS create hot path, all re-probing one volume. Add a per-materialization device-keyed cache: one probe per distinct volume (4N -> ~4). Tests: symlinked-file and symlinked-dir dereference regressions (no leak to primary); APFS volume probed once regardless of copied-path count. |
||
|
|
4a71a0ecb2 |
feat(mobile): add safe Codex rate-limit resets (#9394)
* feat(mobile): add safe Codex rate-limit resets * fix(mobile): address reset credit review feedback * review: purge removed-account reset attempts, shared capability constant, rebase test mocks * review: preserve host compatibility and reset durability * fix(mobile): recover reset capability after cutover * fix(mobile): validate runtime capability payloads * fix(mobile): enforce capability payload contract * fix(mobile): route mock terminals to selected worktree * test(mobile): pin malformed probe retry behavior --------- Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> |
||
|
|
20ceeeb0f4 |
fix(runtime): stop the headless hydration repo gate from dropping every tab (#10437)
* fix(runtime): stop the headless hydration repo gate from dropping every tab #9343 gated headless mobile-session hydration on the live repo list, but read it as `this.store?.getRepos?.() ?? []`. A store that cannot report repos then yields an empty set, which reads as "every repo is gone" and skips every parseable session key — so no tabs hydrate at all. It also called getRepos on every hydrate, including the hot floating-tab poll path that is contractually free of repo/provider inventory work. Resolve the inventory lazily and only for keys that parse to a repoId, and keep `null` (unavailable) distinct from an empty list (all repos really gone). An absent list now fails open; a known list still prunes as #9343 intended. Fixes 3 tests that have been failing on main since #9343 landed: orca-runtime-terminal-retirement (2) and orca-runtime (1). * refactor(editor): extract the pending-focus effect to clear the max-lines cap #8083 pushed RichMarkdownEditor.tsx to 404 counted lines against the 400-line .tsx cap, failing lint for every PR that merges current main. The Explorer find-focus request is self-contained, so it moves to its own hook. |
||
|
|
772081577e |
Fix fork PR/MR worktree creation race via durable review-head refs (#10429)
* Fix fork PR/MR worktree creation race via durable review-head refs
When creating a fork PR/MR worktree, concurrent `git fetch origin` operations
clobber the shared FETCH_HEAD, causing the wrong commit to be checked out.
Fetch PR/MR heads into dedicated per-review refs (`refs/orca/pull/<N>`,
`refs/orca/merge-requests/<N>`) that persist and isolate each head from other
fetches. Gracefully keep the compare-base when the fetch fails but the local
ref already exists, avoiding silent fallback to the wrong branch on transient
network errors.
* Bound PR/MR head fetches with 60s timeout
Prevent PR/MR creation from hanging when a remote is stalled or
unreachable. Both GitHub and GitLab head fetches now enforce a
60-second timeout, matching the bound used in the create-path
fetch. Durable refs (refs/orca/pull/*, refs/orca/merge-requests/*)
decouple the ref from FETCH_HEAD, preserving legacy client semantics.
* test: align CI expectations with main PowerShell/sparse regressions
PR checks merge into main, which recently changed PowerShell launch args
(cwd restore after profiles) and sparse-checkout detection (require
core.sparseCheckout). Derive PowerShell spawn args from the production
resolver, mock the sparse config flag, reset shared worktree list scan
cache between tests, and stop requiring floating polls to avoid getRepos
hydration.
* Address review follow-ups on durable review-head refs
- Unify PR review-head remote selection: local and SSH GitHub paths share
resolveGitHubReviewHeadRemote, which prefers the remote mapping to the
hosting GitHub project (upstream before origin, matching work-item/API
candidate order) so contributor clones fetch refs/pull from the repo
that actually hosts the PR.
- Soft-keep durable review heads: when the PR/MR head fetch fails but
refs/orca/pull/<N> / refs/orca/merge-requests/<iid> still resolves,
keep the pinned SHA (warn) instead of failing resolve, mirroring the
compare-base fallback. Extracted shared compare-base soft-keep into
compare-base-ref-fetch.ts.
- Extract fetchGitLabMergeRequestHeadRef (local + SSH) parallel to the
GitHub helper; bound its local fetch with the shared 60s timeout.
- Share relay-style fetch validation (positive safe-integer id, remote
not starting with "-") between relay and local helpers via
review-head-tracking-ref.ts; move REVIEW_HEAD_FETCH_TIMEOUT_MS there.
- Drop the githubPullRequestHeadLocalRef re-export; resolve head SHAs via
rev-parse --verify <ref>^{commit}.
- Add GitLab anti-FETCH_HEAD regression test plus durable-head soft-keep
and remote-selection unit tests.
Co-authored-by: Orca <help@stably.ai>
* test: supply live getRepos for terminal-retirement hydrates
Main's headless tab hydrate (#9343) skips worktree keys whose repo is not
in getRepos. Retirement tests that rebuild mobile tabs from a persisted
session now advertise the fixture repo as live so PR Checks merge stays green.
* fix(editor): extract RichMarkdownEditor props to stay under max-lines
Main's SSH external-image wiring (#10323) pushed RichMarkdownEditor.tsx over
the 400-line tsx budget, failing PR Checks lint on every merge into main.
Move the props type into a sibling module so the component stays under the
limit without disabling max-lines.
* Make durable review-head refs remote-identity scoped
Embed remote name + URL hash into refs/orca/pull|merge-requests refs to prevent soft-keep from serving wrong project's PR/MR when FETCH_HEAD is clobbered by concurrent fetch. Fetch functions now return the written ref path (writer-authoritative) so callers rev-parse exactly what was fetched, not re-derive identity. Soft-keep only applies to transient errors (timeout, network); fails hard on missing refs, auth failures, and stale relay. Relay returns localRef so client avoids re-hashing (URL normalization can disagree).
---------
Co-authored-by: Orca <help@stably.ai>
|
||
|
|
d1ccfcff40 |
fix(settings): normalize and validate branch prefixes (#7772)
* Normalize branch prefixes and flag invalid ones in settings A custom branch prefix ending in a slash (e.g. "team/") produced a double-slashed branch name like "team//feature" that git rejects, and the raw check-ref-format error gave no hint that the prefix caused it. - Normalize the configured prefix (trim whitespace, strip leading/ trailing and duplicate slashes) in the shared branch-name builder so the common trailing-slash case just works, for local and SSH worktrees. - Validate the prefix on the worktree-create path (computeValidatedBranchName) so a genuinely invalid prefix fails fast with a clear "update it in Settings -> Git" message instead of an opaque git error. - Add a live BranchPrefixFeedback under the Branch Prefix setting: previews the resulting branch name, warns on invalid characters, and notes when a prefix collapses to none. - Keep the background first-work rename on the non-throwing builder since the prefix is already validated at create time. * Keep caret in place when editing the branch prefix The custom branch prefix input was directly controlled by settings, but updateSettings persists through an async IPC round-trip, so the value updated a tick late and React re-assigned it, snapping the caret to the end on mid-string edits. Drive the input from a local draft and only adopt genuine external settings changes so the caret stays put (and fast typing survives slow SSH round-trips). Co-authored-by: Cursor <cursoragent@cursor.com> * Return ReactNode from BranchPrefixFeedback JSX.Element needlessly excludes null/string/number returns; ReactNode keeps the component's return type from over-constraining future changes. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
c2371c0cd8 |
fix: don't hydrate a headless mobile session for a repo that no longer exists (#9343)
`hydrateHeadlessMobileSessionTabsFromWorkspaceSession` iterates
`workspaceSession.tabsByWorktree` with no repo gate. Those keys are
`${repoId}::${path}` and are not pruned when a repo disappears from this
client's view, so a stale key re-materializes a phantom "unknown"/duplicate
workspace with no live repo behind it, on boot, with no `session-created` event.
Skip entries whose parsed repoId is not in the live `repos`; leave unparseable
keys alone (conservative). Defensive complement to the delete-path (#9025) and
load-time (#9200) fixes — this closes the mobile/headless hydration path.
Co-authored-by: fanyunqian.1 <fanyunqian.1@bytedance.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
d8499fae16 |
fix: don't mint worktreeMeta from a sidebar sort-order snapshot (#9342)
`worktrees:persistSortOrder` (ipc/worktrees.ts) and `persistManagedWorktreeSortOrder`
(orca-runtime.ts) call `store.setWorktreeMeta(id, { sortOrder })` for every id the
renderer sends. `setWorktreeMeta` has no repo-existence check, so a stale id the
client still lists — e.g. a removed repo's `${repoId}::${path}` that lingers in the
renderer's order — gets a brand-new `worktreeMeta` entry minted on every sidebar
snapshot, resurrecting an orphan/duplicate workspace on the next launch.
A sort-order snapshot must only reorder worktrees that already exist, never create
one. Guard both call sites with `getWorktreeMeta` so absent ids are skipped.
Co-authored-by: fanyunqian.1 <fanyunqian.1@bytedance.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
817197fc31 | fix(terminal): close tabless PTYs through the live pane path (#9288) | ||
|
|
1b01385872 |
fix(worktree): reconcile symlinked creates by Git identity (#10266)
* fix(worktree): match created worktrees through symlink roots On immutable Linux, /home is often a symlink to /var/home. git worktree list reports the realpath while Orca still holds the /home request path, so creation failed with "Worktree created but not found in listing". After local worktree add, fall back to realpath when string comparison misses. Keep WSL listings on string comparison only (host realpath is not authoritative there). Closes #10170 * test(worktree): harden symlink reconciliation authority * fix(worktree): reconcile creation by Git branch identity * test(worktree): reproduce symlink-root listing with real Git * test(worktree): cover cross-platform reconciliation * fix(worktree): keep reconciliation main-only --------- Co-authored-by: Wooseong Kim <innocarpe@gmail.com> Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> |
||
|
|
e3adb20917 |
fix(agents): include OMP terminals in cold session restoration (#8991)
Preserve OMP session identity and exact resume paths across cold restoration, AI Vault, mobile, WSL/SSH, and host-authority routes. Add mixed-version fallback and completed-session recovery coverage. |
||
|
|
aab112933e |
Revert "fix(memory): bound OOM-prone accumulators (#10179)" (#10255)
Co-authored-by: Orca <help@stably.ai> |
||
|
|
8f40ddf328 | fix(memory): bound OOM-prone accumulators (#10179) | ||
|
|
7ab601487c |
fix(remote): don't classify a stale/gone remote handle as agent completion (#9263)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> |
||
|
|
88b7e69ba1 |
fix(runtime): recover orphan terminals without changing Active Server (#10011)
Adds guarded host-authoritative orphan PTY adoption and keeps Active Server durable preference mutations exclusive to its explicit settings control. |
||
|
|
8685cdb3fb | fix(runtime): stop a throwing client-event listener from wedging worktree sleep + harden fan-out (#10052) | ||
|
|
41751dd90d | fix(runtime): route HUB-owned SSH worktrees through owning runtime (#9994) | ||
|
|
0d82cc3dbb | Fix legacy worktree lineage projection after stable updates (#9913) | ||
|
|
4062ed5a73 |
perf(worktrees): back off worktree scans for agent-scratch repos (#9985)
Production crash diagnostics measured ~128 `git worktree list` execs/min (9,400 in one 80-minute session, ~16% of wall-clock in git subprocesses): the resolved-worktree scan fans out over every registered repo on a 30s cache TTL, and most registered repos on the affected installs were agent-CLI scratch repos (~/.codex-tmp capsules, vendor imports, skill checkouts) that need no freshness. Classify agent-scratch repo roots with a curated shared matcher and stamp their scan-cache entries with a 5-minute TTL instead of 30s. Orca-driven mutations still bypass the TTL via the per-repo generation bump, so only passive pickup of external changes slows for scratch repos. Expected steady-state reduction on the measured install: ~82% fewer git spawns. |
||
|
|
24b8fcc918 |
fix(mobile): restore the last-open tab when returning to a worktree on mobile (#9801)
* fix(mobile): persist per-device tab selection so worktree return restores the last open tab A phone's tab selection lived only in the host's in-memory ClientSessionTabSelectionStore. Any host restart wiped it, and the per-device projection then fell back to deterministic topology, so returning to a worktree on mobile always landed on the first tab instead of the tab last opened on the phone. Persist the per-device selections in the Store (keyed deviceId -> worktreeId), hydrate them when the runtime constructs, and guard projection so an early empty snapshot after restart cannot wipe a hydrated selection before tabs arrive. Selections are pruned with the worktree/repo and on device revoke, and malformed persisted payloads degrade to empty instead of throwing. * fix(mobile): harden persisted tab selection cleanup * fix(mobile): preserve tab selection across worktree rename |
||
|
|
300ee19950 | fix(terminal): make remote workspace sleep converge (#9874) | ||
|
|
b232df732b | fix(terminal): make remote agent sessions host-authoritative (#9687) | ||
|
|
f9f3cd2fbe | fix(terminal): prevent reconnect from killing live daemon sessions (#9804) | ||
|
|
54c1ec5e7a |
fix(remote-runtime): recover terminals after network loss (#9774)
Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> |
||
|
|
a10a2ba53c |
feat(linear): add MCP-style save issue (#9670)
* feat(linear): add MCP-style save issue * fix(linear): harden save issue parity * fix(linear): close save issue contract gaps * docs(linear): bundle project discovery with save issue |
||
|
|
87af1c8673 |
feat(linear): add complete issue relations (#9674)
* feat(linear): add complete issue relations * fix(linear): harden relation reads and writes * fix(linear): classify ambiguous relation writes |
||
|
|
42a4f017b4 | feat(linear): add MCP-compatible issue listing (#9672) | ||
|
|
be066fe8e9 | feat(linear): expose issue activity history (#9667) | ||
|
|
58bcfe2a33 |
perf(runtime): gate PTY path-candidate extraction on mobile-connected (#9422)
* perf(runtime): gate PTY path-candidate extraction on mobile-connected The 3-regex path extractor ran on every PTY chunk of every session for a mobile-only consumer. Now gated behind a sticky mobile-connected flag; first onReady synchronously backfills candidates from the retained 64KB window per original chunk boundary (lazy safety-net fallback). ~28.5us/chunk (~99.8%) saved on the desktop-only hot path; mobile parity preserved (Tier-1 raw-window read unchanged + backfill before any file RPC). 5-round review converged 2-consecutive-clean; independently verified tsc+902 tests. Co-authored-by: Orca <help@stably.ai> * fix(runtime): guard path-candidate activation on the remote runtime proxy MobileSocketWiring.onReady called this.runtime.activateRecentPtyPathCandidateTracking() unconditionally, throwing on the E2EE/remote proxy (which lacks the method) and breaking the remote runtime connection (CI verify failure). Guarded with optional chaining (runtime-rpc.ts:900) — activation is a local-host concern. Mutation-verified E2EE-proxy regression test. Integration test + 3 typechecks green. Co-authored-by: Orca <help@stably.ai> --------- Co-authored-by: Orca <help@stably.ai> |
||
|
|
4a7bdf9177 |
perf(mobile): route graph-sync snapshot fanout through the notify coalescer (#9421)
* perf(mobile): route graph-sync snapshot fanout through the notify coalescer syncWindowGraph no longer unconditionally re-serializes+fans every worktree's mobile snapshot to every client. Serve-hydrate fast path skips the build-then-drop loop when no serve-owned pty exists (desktop case); changed-worktree gating (strict superset of snapshot inputs) routes only real changes through the existing coalescer. No-op syncs 720->0 emits; single-worktree churn 720->120 (~83% fewer bytes to phone). Real changes still propagate. 11-round GPT-5.6-Sol xHIGH review converged 2-consecutive-clean; independently verified tsc+830 tests. Co-authored-by: Orca <help@stably.ai> * fix(mobile): bump snapshotVersion on preserved prune frames + carry split-group layout Pre-merge adversarial review found two stale-phone regressions: (1) preserved-headless snapshots did not bump snapshotVersion, so a pruned-but-serve-preserved worktree change was dropped by clients' same-epoch freshness gate (orca-runtime.ts:22316); (2) serve-only no-op hydrate dropped the phone's split-group layout (orca-runtime.ts:3731). Both fixed + mutation-verified regression tests. 3 typechecks + suites green. Co-authored-by: Orca <help@stably.ai> --------- Co-authored-by: Orca <help@stably.ai> |
||
|
|
22d9c87872 |
Fix Codex automatic resume across account switches (#9696)
* fix(codex): preserve session home on automatic resume * fix(codex): keep automatic resume homes launch-authoritative * fix(codex): resume compressed rollout representations * fix(codex): keep resume migration best effort * fix(codex): reject stale automatic resume provenance * fix(codex): enforce real home on automatic resume * fix(codex): keep automatic resume home authoritative |
||
|
|
e62b7e8172 |
perf(orchestration): skip per-terminal dispatch scan when no dispatches exist (#9694)
Co-authored-by: Orca <help@stably.ai> |
||
|
|
05c32c4757 | fix(runtime): isolate navigation across paired clients (#9664) | ||
|
|
e3c8d96638 |
Access the Floating Workspace from mobile (#8405) (#9523)
* Access the Floating Workspace from mobile (#8405) Surface the desktop Floating Workspace (the global, repo-less scratchpad of terminal tabs under the synthetic `global-floating-terminal` id) on the mobile app so a Claude session left running there is reachable from a phone. Adds a terminal-icon button to the mobile host header (phone + tablet sidebar) that opens the existing Session screen for the floating id. The sentinel already had host-side RPC support (#5946: local runtime, homedir cwd, explicit-id fast paths in session.tabs.*); this wires up the mobile surface and gates it on a new `floatingWorkspaceEnabled` status flag so the entry hides on hosts that predate it or where the feature is disabled. The Session screen learns an `isFloatingWorkspaceRoute` flag (mirroring the existing `folder:` route pattern) that hides repo-backed surfaces — Files, Source Control, PR/checks, agent history — skips the diff-comment and GitHub probes, routes terminal URL taps to the phone browser, and limits the New Tab drawer to terminals + agents (browser/markdown creation resolves a real worktree host-side and stays desktop-only). useLiveWorktreeName short-circuits for the sentinel so it no longer polls worktree.show forever. Extracted the host status.get gating into a useHostStatusGates hook to keep the host screen under the max-lines ratchet. * Harden mobile Floating Workspace routing * Fix mobile host gate reuse race * Harden floating mobile session polling * fix(mobile): harden floating workspace route reuse * fix(mobile): skip floating workspace repo lookup * fix(mobile): clarify floating workspace header action |
||
|
|
cc44acaaa3 | Fix Windows ConPTY OSC color reply leaks at the PTY owner (#9651) | ||
|
|
971b167548 |
fix(github): load PR diffs for Enterprise remotes (#8932)
* fix(github): load PR diffs for Enterprise remotes * fix(github): encode PR content paths by segment * Fix PR review actions failing on GitHub Enterprise remotes - Threads GitHub host identity (not just owner/repo) through the client, work-item-details, issues, and RPC layers so gh commands target the correct Enterprise server instead of silently falling back to github.com - Adds a shared github-api-repository helper to resolve/host-qualify repo identity consistently across REST, GraphQL, and CLI shorthand calls - Scopes the gh rate-limit breaker and singleton rate-limit snapshot by host/runtime so a github.com block or probe can't affect GHES or WSL - Coalesces concurrent host-auth probes and paginates PR file fetching beyond 100 results - Propagates `host` through renderer PR caches, checks-panel keys, and preload IPC types so Enterprise and github.com data never collide * Route gh host qualification through runner options instead of argv sniff Move GHES/GH_HOST resolution from parsing --hostname/--repo out of gh argv to an explicit options.host passed through ghExecFileAsync, since SSH-backed repos spawn gh with no cwd and argv sniffing couldn't reliably detect the target host. The runner now injects --hostname and qualifies --repo/-R at spawn time from options.host, and rate-limit scoping/guards use the same explicit host instead of inferring it. Also adds a shared githubRepoIdentityKey helper to keep cache/store keys consistent with the new host-aware repository identity. * Fix gh CLI GHES host pinning and rate-limit scope leaks - Pin `--host` on every gh call site so a process-level GH_HOST can't silently redirect requests, and qualify `-R`/`-R=` repo shorthand alongside the existing `--repo=` handling. - Check the target scope for an active rate-limit block before each WSL/native or host fallback retry, not just on the initial attempt, so a blocked scope can't be hit again through a fallback path. - Compute idempotency once per call instead of re-deriving it after fallback reassigns args. * Fix GitHub Enterprise host identity loss across PR/work-item paths - Thread `host` through mobile PR RPC params, IPC work-item lookups, and RPC schemas so GHES identity survives the renderer/mobile/main boundary instead of silently falling back to a same-named github.com repo. - Qualify `--repo`/`-R` args for github.com too (not just GHES), since gh resolves bare shorthand against a process-level GH_HOST that can redirect pinned github.com commands. - Cache `getOriginGitHubApiRepository` to avoid a per-call uncached `git remote get-url` round trip on connection-backed repos. - Add a local-fork fallback in `getWorkItemDetails` so PRs living on a base repo (not visible via the origin slug) still resolve via cwd. - Centralize the github.com-vs-GHES host predicate in `isDefaultGitHubHost` so cache keys, quota scoping, and identity checks can't drift out of sync. * Make repository identity host-aware across all GitHub surfaces Generalize the auth-gated enterprise resolver to any remote and build a cached hosted-identity family (origin/issue/candidates/source) on top of it, then migrate every github.com-only consumer: Tasks listing/counting, branch-to-PR discovery, push targets, fork upstream, issue operations, Projects, web links, avatars, and PR-link facts. Scope the rate-limit breaker probe per runtime:host and classify WSL UNC cwds correctly. Co-authored-by: Orca <help@stably.ai> * Fix expected slug to include host field in GitHub PR link test Updates the smart-source paste-intent test fixture to match the repository slug shape that now carries a `host` field, keeping GHES host identity intact through the paste-intent parsing path. * Surface per-host gh auth state for GitHub Enterprise diagnoseGhAuth accepts the host a surface needs credentials for, scopes the account/scope diagnosis to that host, and reports whether gh has any login there; GhAuthErrorHelp renders host-qualified login/refresh commands so an unauthenticated GHES host stops masquerading as a github.com scope problem. Also fixes the mobile paste-intent expectation for host-carrying parsed links. Co-authored-by: Orca <help@stably.ai> * Bound GHES identity caches and preserve non-default ports in host identity Cap the origin-repo and host-auth caches like ownerRepoCache; keep ports from remote/link URLs so GHES on a non-default port is a distinct identity; make positional github.com slugs explicit against GH_HOST; compare work-item sources by host-aware identity key; bail cwd-less branch lookups when no repository candidate resolved; thread host through the renderer work-item slug lookup. Co-authored-by: Orca <help@stably.ai> * Thread GitHub host through issue detail requests Incorporates ghes-issue-host-support (ed6bb96ef): one hosted issue repository identity is resolved before the details fan-out so comments, timeline, participants, and mention lookups cannot drift across hosts, with SSH guards so unresolved issue/PR repositories never fall through to gh's default host. Co-authored-by: Orca <help@stably.ai> * Scope remaining GitHub rate-limit accounting * Resolve typed PR lookups across hosted repository candidates getWorkItem's PR path probes upstream-then-origin hosted candidates instead of origin alone, so fork checkouts resolve the base repo's PR with the right host; issue detail resolution reuses the up-front hosted identity and keeps the SSH unresolved-host guards. Co-authored-by: Orca <help@stably.ai> * Refactor GitHub repository execution setup * Carry host on smart-submit link intents Co-authored-by: Orca <help@stably.ai> * Carry the project host on GitHub item dialog origins Co-authored-by: Orca <help@stably.ai> * Keep GHES web ports but drop SSH transport ports in host identity Supersedes PR #9118 on this branch: http(s) remote ports identify the Enterprise web/API endpoint and are preserved, while ssh/git transport ports (including ssh.github.com:443) never leak into gh's host identity. Replaces the ssh.github.com:443 special case with the structural protocol split and ports the PR's parsing test suite. Co-authored-by: Orca <help@stably.ai> * Support GitHub Enterprise diffs and mutations with host-scoped caches Parse GitHub host identity from work-item URLs and carry it through PR/issue mutations, labels, and assignments. Bound rate-limit and scope-probe caches (1024 and 512 entries) to prevent unbounded growth when interacting with multiple GHES instances. Normalize repository identity keys to include host so github.com and GHES slugs don't collide in cache and equality checks. * Support GitHub Enterprise diffs and mutations with host-scoped caches - Carry host identity through PR mutations and reads so fork PRs on different GHES instances don't collide in cache or state tracking. - Validate host authentication before routing requests to unconfigured Enterprise servers; ambient credentials must never reach untrusted hosts. - Scope rate-limit guards and spend tracking per host so GHES quota stays independent from github.com quota. - Respect explicit --hostname arguments in gh CLI calls ahead of GH_HOST or ambient defaults, so breaker state follows the actual request target. - Detect implicit WSL runtimes from UNC paths for consistent host auth and execution-options scoping across mobile and desktop clients. * Support GitHub Enterprise work-item diffs with host-scoped execution Enterprise PRs must use their selected host consistently across diff, comments, and file-content loads. Validate repository slugs before authenticated execution to prevent path-injection via renderer overrides. Scope project browsing cache and rate-limit tracking by host to prevent cross-host pollution. Use parsed URLs as authoritative over ambient hosts for project resolution. * Support GitHub Enterprise work-item diffs with host-scoped execution Preserve host identity on PR/issue work items throughout the mutation and diff pipeline so Enterprise instances (including ported endpoints like github.acme.test:8443) can execute mutations without ambiguity. Rate-limit gh commands by the pre-qualified --repo host, cache auth state per ported host, and surface Enterprise hosts in project metadata and error messages. * fix(review): drop dead rateLimitGuard/noteRateLimitSpend re-export Both callers (project-view.ts, mutations.ts) moved to the host-scoped repositoryRateLimitGuard/noteRepositoryRateLimitSpend; the bucket-only re-export in internals.ts had zero importers left. Co-authored-by: Orca <help@stably.ai> * fix(ci): split Enterprise host work-item tests under max-lines Move GHES/SSH host-routing cases out of work-item-details.test.ts so the suite stays within the 800-line test max-lines budget. * test(github): align mocks with host-scoped repository resolution - Route origin repository resolution through getOwnerRepoForRemote, not getOwnerRepo, to match production path - Pin github.com host on origin results so host-less fixtures pass host gate in resolveGitHubApiRepository - Add generation-based invalidation to prevent stale slug-cache writes from in-flight resolutions - Fix ref-sync race in ProjectPicker: use useLayoutEffect so committed tree owns browse cache key - Defer handledCrossRepoUrlRef assignment in SmartWorkspaceNameField until resolution succeeds - Update Enterprise host routing: found work items must not silently fall back to default host when unresolved - Normalize GHES avatar URLs: accept explicit port 443 as canonical form, not a fallback trigger --------- Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com> Co-authored-by: Orca <help@stably.ai> |
||
|
|
3468b434d6 |
feat(dashboard): add agent dashboard popout (#9604)
* feat(dashboard): add agent dashboard popout * fix(dashboard): gate and harden agent popout * test(ipc): isolate dashboard handler registration * fix(dashboard): drop diff status from bucket counts * fix(dashboard): address review feedback * perf(dashboard): ignore unrelated store churn |
||
|
|
0ed1d04b3b |
fix(codex): migrate legacy shared-home sessions before resume (#9624)
* fix(codex): migrate legacy shared-home resumes * fix(mobile): allow legacy Codex resume preparation |
||
|
|
f655ae3929 |
fix(pi): resolve OMP tab identity from the outer wrapper, not the wrapped pi (#6364) (#9600)
* fix(pi): resolve OMP tab identity from the outer wrapper, not the wrapped pi (#6364) OMP runs as a `shell → omp → pi` process tree and Orca recognizes both `omp` and `pi` as distinct agents. The foreground-process readers scored the deepest `+` foreground descendant, so they returned the wrapped `pi` engine instead of the `omp` the user actually launched — and because the sampled `+` frame moves between omp and pi across command boundaries, the read oscillated and the tab icon/label flickered OMP↔Pi. It surfaced on Remote Host / SSH (only in a worktree, only while working) where a mirrored/restored pane has no client-side launchAgent to anchor identity, so the (host-read) foreground was the only pi/omp signal — and it was wrong. Fix the detection at its source so foregroundAgent is trustworthy everywhere (local tab icon, exit detection, and the remote mirror): - Add `resolveOuterWrapperForegroundProcess` (src/shared/foreground-wrapper-agent.ts): when the recognized foreground winner shares a `titleIdentityGroup` with a shallower recognized ancestor, return the shallowest — the outer wrapper. Stable regardless of which of omp/pi holds the foreground at the sample; bare Pi and cross-group reads (Codex, etc.) are unchanged. - Wire it into both readers: local `agent-foreground-process.ts` and the SSH relay `pty-shell-utils.ts`. Also harden the relay's fallback short-circuit so a pi-compatible process reported by node-pty rescans for the omp wrapper instead of trusting `pi` raw (never downgrading below the fallback). Also unify the host-side publish path onto the shared owner resolver: the mobile session snapshot builders now resolve pane ownership via `resolvePaneAgentOwner` ({launchAgent, hookAgent}) instead of a bespoke `launchAgent ?? foregroundAgent`, so the host stops being a divergent fourth identity path and the hook's host-stamped identity survives when launchAgent is dropped on a mirrored pane. Tests: unit coverage for the wrapper resolver, `shell→omp→pi` ps trees for both readers (outer omp, oscillating-frame, bare pi, node-pty pi fallback), and a host-snapshot regression that keeps an OMP hook labeled OMP when the wrapped pi child owns the foreground. * fix(pi): harden wrapper foreground ownership * fix(pi): preserve OMP ownership in daemon foreground reads * fix(pi): retain OMP owner when process scans fail |
||
|
|
e58de71f5e |
feat(codex): real-home routing + self-contained multi-account homes (#9501)
* feat(codex): backfill managed-home sessions into the real Codex home once per host Orca-launched Codex sessions currently land only in the Orca-managed runtime home, so the user's own `codex resume` picker and app history never see them (#4444, #8612). Backfill the managed sessions tree into the real ~/.codex/sessions/YYYY/MM/DD layout once per host: - hardlink first (one physical rollout log), copy as the cross-volume fallback; existing target files are always skipped, nothing in either home is deleted or moved - idempotent; per-file failures leave the completion marker unset so the next startup retries cheaply - JSONL audit log of every link/copy/failure under <userData>/codex-session-backfill/ - honors the custom Codex session source home override, mirroring the existing system->managed bridge WSL managed homes are distro-local and need an in-distro variant; that is a follow-up. * feat(codex): flag-gated system-default real-home routing scaffolding Staged internal flag (default OFF, no settings UI): route the SYSTEM-DEFAULT Codex account at the user's real ~/.codex instead of Orca's managed runtime home. Flag OFF is byte-identical to today; managed (multi-account) selections are unchanged in either state. Routing (flag ON + host system default = no managed account): - CodexRuntimeHomeService.prepareForCodexLaunch / prepareForRateLimitFetch return null so the PTY/env layer injects no managed CODEX_HOME and the rate-limit fetcher + auth-presence gate fall back to ~/.codex (the background poller stops spawning Codex against the managed home — the #5370 auth war). - buildPtyHostEnv strips only a nested-Orca-inherited Orca-owned override (CODEX_HOME matching the private ORCA_CODEX_HOME marker), preserving a user-set CODEX_HOME. Shell-ready re-exports already no-op without the marker. - The headless commit-message Codex path strips the same inherited override. Hook install for the real-home lane (append-last into ~/.codex/hooks.json, trust via the app-server client) lands with the trust plumbing; the managed hook install is skipped for this lane meanwhile. Credit @jellychoco (#8606) for the native-home routing direction. Depends on the codex trust-rpc-grant plumbing for the real-home hook installer. * fix(codex): strip the daemon-inherited Orca CODEX_HOME override for real-home routing The daemon spawns PTYs from its own inherited environment and honors only spawnOptions.envToDelete, so mutating the sparse env object was not enough to strip an Orca-owned CODEX_HOME the daemon already carries. Add the strip to envToDelete for both daemon host-spawn paths, preserving a user-set CODEX_HOME. Verified live via CDP against a sandboxed dev instance (flag ON): an Orca-spawned pane reports empty CODEX_HOME/ORCA_CODEX_HOME, so Codex resolves its own ~/.codex. Adds daemon-path unit coverage (strip Orca-owned, preserve user-owned, no-op when flag OFF). * fix(codex): harden one-time session backfill * test(codex): cover staged cross-volume install * feat(codex): app-server trust-grant client, capability cache, and grant ledger Short-lived codex app-server JSON-RPC client (hooks/list + config/batchWrite, the same pair the Codex TUI 'Trust all' flow calls), run in a bundled ELECTRON_RUN_AS_NODE entry so synchronous launch prep can block on it with a hard deadline and guaranteed child reap. Capability cache modeled on GitCapabilityCache, scoped per execution host (native vs each WSL distro), with a narrow unknown-method/missing-subcommand unsupported predicate. The grant ledger records verified grants so steady-state launches skip the RPC. * fix(codex): grant managed hook trust via codex app-server RPCs in install/refresh Host and WSL installs now grant trust for Orca's managed status hooks through codex's own hooks/list -> config/batchWrite -> re-list verify, scoped to exactly the managed entries; the previous computeTrustedHash lane is the unchanged fallback for incapable/erroring CLIs. getStatus and the removal paths recognize ledger-recorded codex hashes so drift between codex's real algorithm and the replica no longer misreports or strands trust. SSH remote install is untouched by design. * test(codex): cover app-server trust grant client, cache, ledger, and lanes * test(codex): cover commit-message real-home override strip/preserve Adds the two cases for the headless commit-message Codex env under real-home routing: a nested-Orca-inherited Orca-owned CODEX_HOME is stripped, and a user-owned CODEX_HOME is preserved. * test(codex): WSL grant-lane coverage — in-distro invocation and fallback parity * feat(codex): real-home hook installer trusted via the codex app-server grant client With the real-home flag ON and the system-default selection, install Orca's status hook into the user's real ~/.codex before any pane spawns: - entry APPENDED LAST per managed event: codex hook trust keys are positional (source:event:group:handler), so appending keeps every user entry's position and trust record intact; user entries and unknown top-level hooks.json fields are preserved verbatim - trust is granted exclusively through the codex app-server client (hooks/list + config/batchWrite, verified by re-list); Orca never writes [hooks.state] into the user's real config.toml itself - if the grant lane is unavailable (old binary, unsupported RPC, verify failure), the appended entry is rolled back byte-exactly and the host keeps the managed-home lane end to end (PTY env, rate limits, commit messages) via a lane gate on the runtime-home service - one-time pristine backup of the user's hooks.json under Orca's userData; a rolling .bak sits next to the file (existing atomic writer) - hook opt-out sweeps Orca entries from the real home and drops Orca-owned trust records; flag-off downgrade re-arms the existing legacy system-home sweep, which removes the entry and its trust keys cleanly - the legacy system-home sweep is suppressed only while the real-home lane owns ~/.codex/hooks.json, so managed installs cannot delete the entry * fix(codex): resolve the trust-grant entry without requiring electron The grant bridge is reachable from plain-Node CLI entries, where the plain-node entry guard rejects any chunk containing require("electron"). Resolve the bundled session entry from __dirname (root chunk and chunks/ layouts) with an app.asar -> app.asar.unpacked rewrite for packaged runs, instead of electron's app path APIs. * fix(codex): keep session backfill off main thread Use asynchronous, sequential filesystem operations for the one-time rollout backfill, and avoid repeated target-directory probes. Treat inaccessible managed session roots as retryable failures instead of writing a false completion marker. * fix(codex): harden app-server trust grant fallback * fix(codex): install cross-volume session backfill copies atomically On a real Codex home whose filesystem supports no hardlinks (exFAT/FAT, some network mounts), the staged cross-volume copy was installed with a non-atomic copyFile(..., COPYFILE_EXCL) straight into the final rollout-*.jsonl name. An install interrupted mid-copy (app quit, crash, ENOSPC during the deferred run) could strand a truncated rollout that the next run then skips as already-present, defeating the staging design's own guarantee that a failed copy never leaves a partial session behind. Install the fully-staged copy with an atomic rename instead, guarded by an existence re-check so it keeps the never-overwrite contract (and the rename source is the same immutable managed rollout, so any clobber would be byte-identical). Cover the no-hardlink-support target and an interrupted install that must leave no partial in the user's sessions tree. * fix(codex): resolve grant entry from __dirname so plain-node CLI entries stay electron-free The build guard rejects any electron require reachable from plain-node entries; the bridge now maps app.asar to app.asar.unpacked by string replacement instead of consulting electron app paths. CLI typecheck project lists the new trust-grant module graph. * fix(codex): harden trust grant reconciliation * fix(codex): restore trust config permissions on rollback * fix(codex): harden real-home routing cleanup and retries * fix(codex): preserve unicode trust RPC responses * fix(codex): preserve remote env and complete real-home cleanup * fix(codex): preserve real-home lane invariants * test(terminal): isolate replacement idle reset assertion * fix(codex): preserve real-home dotfile links * fix(codex): preserve verified trust grants across launch prep * fix(codex): preserve dangling config symlinks on rollback * fix(codex): don't revoke a just-granted WSL home on a false 'missing' probe The async wsl.exe canonical-path settlement could report the runtime home 'missing' immediately after a verified RPC grant (a false negative — codex had just written and re-listed trust there), which drove the reconciliation 'remove' branch to delete all six granted [hooks.state] tables, leaving a bare [hooks.state] the launching pane read as 'hooks need review'. A 'missing' settlement now revokes only when no successful install ran this generation; a genuinely moved home still resolves to a different path and reinstalls. * test(codex): model codex config/batchWrite faithfully on Windows The grant-lane stub simulated codex by calling Orca's upsertHookTrustEntries, which writes both separator variants for a Windows key (a fallback-lane compat shim real codex never does) — fabricating duplicate tables and whitespace the RPC path never produces, so the byte-stable and no-duplicate assertions failed on win32. Replace it with a single-variant, blank-line-separated writer that matches the real 0.144.x binary's output. * feat(codex): collapse duplicate session listings across Codex roots Backfilled/bridged rollouts are hardlinked into both the real ~/.codex and Orca's managed runtime home, so AI Vault listed each session once per root (#7521). Dedup candidates by rollout file name pre-parse and parsed sessions by session id post-parse, keeping the canonical root: host real home first (unprefixed resume), then the managed runtime home, then other homes. Applies to local, WSL, and SSH-remote scans. * feat(codex): background sqlite index heal for backfilled sessions Codex's own state-DB metadata backfill is one-shot, so rollouts hardlinked in by Orca's session backfill never become visible to Codex's DB-driven surfaces. Extract the app-server stdio JSONL transport into codex-app-server-session (shared with the trust-grant client) and add a bounded, resumable background pass that drives Codex's lazy indexing via thread/read per backfilled session: recent-first, batched onto one short-lived server per batch with small concurrency, ledger + marker so steady-state startups are a no-op, stop-aware on quit, and capability-aware on CLIs without the app-server surface. * fix(codex): preserve session identity during dedup heal * fix(codex): preserve user trust during real-home cleanup * fix(codex): harden real-home heal boundaries * fix(codex): fail closed on unsafe backfill install * fix: harden real-home hook cleanup * fix(ai-vault): preserve execution boundaries and reap children * fix(codex): narrow app-server unsupported detection * fix(codex): bound user hook trust rebase retries per host The rebase lane ran a codex app-server session on every launch prep while a host was stuck (CLI without app-server support, or keys hooks/list cannot match). Gate the transaction on the shared capability cache and add the same 5-minute transient cooldown the grant lane uses, so sweep and legacy-cleanup retries cost plain fs reads instead of a codex session per pane spawn. * fix(codex): enforce real-home resume and heal boundaries * fix(codex): establish real-home lane before cleanup * fix(codex): stop index heal before delayed spawn * fix(codex): protect symlinked rolling backups * fix(ai-vault): preserve resume env deletion through drag * fix(codex): strip inherited Codex homes on mobile real-home resume The mobile resume surface types a bare real-home codex resume into a freshly created pane, but never asked for CODEX_HOME/ORCA_CODEX_HOME deletion at pane spawn, so an agentDefaultEnv-pinned or daemon-inherited Codex home rerouted the resume away from the user's real ~/.codex while the same session resumed correctly on desktop. Share the deletion helper from the AI Vault resume builders and forward it through the mobile launch and session.tabs.createTerminal call. * fix(codex): gate session migration on real-home lane * fix(codex): stop session backfill after opt-out * fix(codex): keep session heal failures retryable * fix(codex): keep session migration state recoverable * fix(codex): retry republished missing session heals * fix(codex): preserve hook symlink trust path * fix(codex): disambiguate POSIX trust paths * fix(codex): align hook trust source paths * fix(codex): harden trust grant lifecycle * fix(codex): restore envToDelete on client invocation type after base reconcile * test(codex): type child.stdout as PassThrough for oversized-output write * Assemble RC: reconcile app-server transport API across PRs Unify on the object RPC surface from the index-heal transport (#8921) while preserving the default-home env strip (#8828) and the narrowed missing-app-server capability signal (#8847): adapt the user-hook-trust-rebase consumer + tests, port envToDelete stripping into the shared session, and route stderr classification through the canonical capability-signal module. * RC: enable system-default real-home routing by default (flag ON) Flip codexSystemDefaultRealHomeEnabled to default ON for this RC's staged rollout (a user can still opt out by setting it false, which stays byte-identical to managed-home behavior). This is the only intended behavior difference between the RC branch and the individual PRs. Updates the two tests that assumed the prior OFF default. * fix(codex): snapshot hooks.json bytes+parse in one read to close real-home clobber race The install/sweep/legacy-cleanup paths parsed hooks.json, then did a separate later read to capture the previous bytes for the pre-write generation guard. A concurrent save (second Orca instance or the user editing the file) could land between the parse and that second read and be silently overwritten. readHooksJsonWithRaw returns the raw bytes and parse from a single read so the guard compares against exactly what it parsed. Adds a regression test that mutates hooks.json mid-RPC and asserts the sweep aborts without clobbering. * fix(codex): sanitize managed account config trust * fix(codex): guard OAuth add for custom providers * fix(codex): persist outgoing managed tokens before real-home lane takeover (PR-C) prepareForCodexLaunch returns null early for the real-home / system-default lane before syncForCurrentSelection runs. If a managed account is still recorded as synced when the selection has dropped to the system default (nulled without a sync pass, or auto-deselect on missing managed auth), a Codex-refreshed token stranded in the shared runtime home is never persisted to its canonical per-account home -> token loss. Read the outgoing managed account's refreshed token back before the real home takes over. The real-home lane implies host === null, so running the managed->system-default transition restores only Orca's runtime mirror from ~/.codex and never writes the real ~/.codex. It is a no-op once the selection has already been reconciled, so the normal select path does not double-write. * fix(codex): preserve refreshes across all default transitions * feat(codex): show system-default/real-home account identity in switcher (PR-B) The account switcher modeled the system-default Codex account as activeAccountId:null with no identity fields, so the null row rendered blank ("System default" / generic subtitle) even though its effective login is whatever ~/.codex/auth.json currently is. Add a CodexSystemDefaultIdentity descriptor {hasAuth, authKind, email, providerAccountId, workspaceLabel} to CodexRateLimitAccountsState, resolved live and READ-ONLY from ~/.codex by the accounts service and returned from listAccounts()/getSnapshot(). The settings switcher now renders the null (system-default) row as that real identity: the OAuth email when signed in, "Custom provider — no usage tracked." for env-key/custom-provider logins (auth.json with OPENAI_API_KEY, or an OPENAI_API_KEY env with no auth.json), and the generic fallback when signed out. Identity is host-scoped (per-distro WSL keeps the generic label). Orca never writes ~/.codex; managed-account switches only touch Orca-owned homes, so the system-default identity stays a stable, displayed source of truth. Usage already routes to the real home via getSystemCodexHomePath, so the switcher now attributes it to a real face. Tests (sandboxed temp homes only): OAuth email/provider resolution, api-key auth.json and env-key (no auth.json) as custom-provider, signed-out, and select/deselect of a managed account never mutating ~/.codex/auth.json. * fix(codex): parse multiline provider pins in OAuth guard * fix(codex): harden managed trust sanitization * fix(codex): harden system-default identity rendering * feat(codex): give each managed account a self-contained CODEX_HOME; retire shared mirror (PR-E) With the real-home flag ON, a host managed account now launches directly against its own codex-accounts/<id>/home instead of the shared runtime mirror + auth.json hot-swap: - codex-home-paths: syncSystemCodexResourcesIntoManagedHome links system resources into any managed home (ownership-marker discipline; never symlinks into / mutates ~/.codex). - runtime-home-service: prepareForCodexLaunch / prepareForRateLimitFetch / syncForCurrentSelection route the per-account home directly and skip the shared-home hot-swap + token read-back; each home keeps its own auth in place (fixes GAP-5 concurrent auth race). Session discovery scans every per-account home. - hook-service / hook-trust-promotion: install/getStatus/refresh accept a runtimeHomePath so hooks + RPC-granted trust land in the per-account home. - service: config mirror into a self-contained home uses the trust- preserving merge so granted hook/project trust survives account switches. - codex-session-root-dedup: rank codex-accounts/<id>/home as canonical managed alongside the shared runtime home. Flag-OFF and the system-default real-home (null) lane are unchanged; the nested-Orca CODEX_HOME===ORCA_CODEX_HOME daemon strip (#5370) is preserved. Sandboxed tests only; ~/.codex is never mutated. * fix(codex): validate per-account home ownership * fix(codex): keep managed rollouts discoverable across real-home opt-out WI-4 lossless migration/rollback validation for pre-E shared-mirror managed accounts. Session discovery gated the per-account home scan on the real-home flag, so opting back out (flag OFF) hid every rollout an account accumulated while the flag was ON — the data stayed on disk but vanished from the AI Vault until the flag flipped back on. Scan a managed host home whenever it holds a sessions/ tree, independent of the flag; a never-enabled install keeps its homes credential-only so opt-out stays byte-identical to today. Forward migration was already lossless (the shared mirror is always scanned) and the opt-out credential read-back already refuses to overwrite a fresher per-account token; add tests locking all three invariants. Sandboxed tests only; ~/.codex is never touched. * fix(codex): migrate stranded shared auth on E takeover * test(e2e): isolate Electron from developer Codex home * test(codex): add real-account validation harness * fix(codex): finish C and E matcher composition * fix(codex): bound validation harness shutdown * test(codex): isolate hook lifecycle user data * test(codex): cover realistic account-home migration * fix(codex): keep standalone home tripwire active * test(codex): fingerprint system auth in validation reports * fix(codex): bind managed homes to account ownership * fix(codex): normalize Windows trust source identity * fix(codex): make Windows trust upgrade transactional * test(codex): use TypeScript pipeline for validation scripts * test(codex): run validation modules through native node * test(codex): allow slow Windows tripwire startup * fix(codex): survive lingering Windows codex login processes in add-account On Windows, codex login can keep running (with descendants) after it has written auth.json, holding OS handles on the per-account managed home (log/codex-login.log). That made doAddAccount's post-login cleanup fail with ENOTEMPTY (rmSync) and left an orphaned codex-accounts/<id>/home. - runCodexLogin now watches for auth.json on Windows and force-kills the login process tree (taskkill /t) if it lingers past a short grace period; the forced exit is treated as a successful login. The 120s timeout path also kills the whole tree instead of only the direct child. macOS/Linux behavior is unchanged. - safeRemoveManagedHome now removes homes with rmSync maxRetries / retryDelay (mirroring the local-worktree-filesystem Windows policy) and no longer lets a cleanup failure mask the original add error. - run-codex-real-account-validation.mjs accepts --temp-parent / ORCA_CODEX_VALIDATION_TEMP_PARENT so the disposable root can live outside %USERPROFILE% on Windows, and fails with an actionable message before creating anything when the temp parent is inside the primary home. The real-home guard is unchanged. * fix(codex): preserve managed-account MCP .credentials.json on per-account-home migration (#8440) Codex file-mode MCP OAuth tokens live in $CODEX_HOME/.credentials.json, keyed by MCP server URL with no account identity of their own. The legacy shared-mirror -> per-account-home migration only carried auth.json, so an existing managed account with authed MCP servers had its tokens stranded on upgrade and silently needed re-auth. Carry the shared mirror's .credentials.json into the same identity-proven per-account home alongside auth.json: only into the single uniquely-matched active account (no cross-account leak), only when the destination has none yet (never clobber a newer file the account authed in its own home), atomic 0600, absent-source no-op. New MCP auth already lands in the per-account home since that home is CODEX_HOME. * fix(codex): preserve Windows reauthentication login flow * test(codex): build real-account validation harness cross-platform on Windows The harness built its app with execFileSync('npx', ['electron-vite', ...]), but npx resolves to a .cmd shim on Windows that execFileSync cannot launch (ENOENT), so the harness could not build its own app there and required --skip-build with a prebuilt out/main/index.js. Extract resolveElectronViteBuildCommand(repoRoot): it runs the repository-local electron-vite JS entry (node_modules/electron-vite/bin/electron-vite.js) with the current Node binary (process.execPath), which resolves identically on macOS, Linux, and Windows with no shell. It throws a clear error if the local entry is missing (install deps or pass --skip-build). --skip-build behavior is unchanged. Add regression coverage asserting the build command uses process.execPath and the repo-local JS entry (not npx), and that a missing entry fails clearly. * fix(codex): version the MCP creds migration independently of the auth marker The auth carry and the MCP .credentials.json carry (#8440) shared one existence-only v1 marker, so any build that stamped the auth-only marker first would strand the MCP store forever. The MCP carry now concludes via its own per-account-mcp-creds-migration-v1.json marker and runs even when the auth marker is already present; ordering is code-enforced instead of landing-discipline-enforced. Also isolate per-account read failures: one stale or deleted account home no longer aborts the whole migration. The broken account stays in the unique-identity ambiguity gate via its stored fields but is never read or written, so the active account still migrates. * fix(codex): fail corrupt managed auth.json without echoing credential bytes A raw JSON.parse SyntaxError from loadOAuthCredentials could carry auth file fragments into logs and the add/reauth error surface. Throw a sanitized error instead; filesystem errors still propagate unchanged. * fix(mobile): give the pairing runtime a disposable home for the E2E boot guard The main-process guard now refuses to start with ORCA_E2E_USER_DATA_DIR set but the real user home, and this was the one caller not updated — the temporary pairing runtime crashed before emitting its pairing URL. * test(codex): canonicalize harness containment guards and retry cleanup Resolve symlinks before the disposable-root containment checks so a symlinked temp parent cannot smuggle the throwaway home inside the primary home, and give the final cleanup rm Windows retry/force so a briefly lingering codex handle cannot strand the credential-bearing root. * test(codex): add lane-aware containment mode to the real-account harness The Windows gate-D run proved strict zero-event whole-profile containment is structurally unreachable with the real-home flag ON: system-default spawn sites deliberately delete CODEX_HOME so native codex resolves the real ~/.codex, and on Windows the binary ignores the USERPROFILE sandbox. Its own volatile runtime churn (root sqlite/WAL/SHM, tmp/, log/) is the shipped Phase-1 design, not a candidate defect. --lane-aware-containment records those designed events without aborting while every other real-home write — auth.json, config.toml, .credentials.json, hooks.json, sessions/, anything unknown — remains a hard violation and still aborts the run. Default behavior is unchanged (strict); the absolute zero-event claim stays carried by macOS runs, where HOME does sandbox native codex. * test(codex): allow the real-account harness to pin the real-home flag off --system-default-real-home off seeds and env-pins the flag OFF so every codex spawn gets an explicit managed CODEX_HOME and native codex never resolves the OS profile. This is the only Windows configuration where the strict zero-event whole-profile tripwire is reachable, and it matches the stable-rollout default; flag-ON runs keep lane-aware classification. * test(codex): correct the flag-off harness comment to kill-switch rationale The rollout ships all codex-home changes at once (no phased rollout), so flag OFF is the emergency kill-switch lane, not the stable default. * test(e2e): canonicalize the isolated E2E home path The disposable HOME lives under os.tmpdir(), whose spelling is an alias on CI (macOS /var symlink, Windows 8.3 RUNNER~1). Git canonicalizes worktree paths, so worktrees created under the aliased home never matched the app's listing — golden core flows and the packaged crash-survival harness failed with 'worktree created but not found in listing'. Resolve the home to its canonical spelling at creation in both the e2e helper and the packaged-app driver. * fix(codex): address CodeRabbit review on the landing PR - carry envToDelete through the mobile agent-resume startup plan so a real-home Codex resume cannot inherit an ambient CODEX_HOME - strip Orca-owned Codex overrides in the commit-message WSL fallback, matching the host fallback - strip ELECTRON_RUN_AS_NODE in the computer-e2e driver like every other home-isolation caller - drop the unused hooksEnabled parameter from isRealHomeCodexHookLaneUsable * feat(codex): ship real-home routing unconditionally, remove the rollout flag The codexSystemDefaultRealHomeEnabled setting is gone from types and constants and the helper no longer consults settings — the system-default real-home lane and per-account homes ship for everyone in one release. This also un-strands profiles that rc-era builds stamped with false (the setting had no UI, so every stored false was a seeded artifact that would have silently kept those users on the legacy mirror forever). The ORCA_CODEX_SYSTEM_DEFAULT_REAL_HOME env override survives strictly as a test-rig control: the containment harness pins the legacy lane for strict zero-event Windows runs, e2e home isolation pins lanes inside disposable homes, and the legacy-lane test suites now route their per-test lane selection through it. --------- Co-authored-by: OrcaWin <alpha-eng@stably.ai> |
||
|
|
1d2ba1e39d |
fix: never surface coding-agent scratch worktrees in the sidebar (#9535)
* fix: never surface coding-agent scratch worktrees in the sidebar Sub-agent runs (e.g. Claude Code worktree isolation) create throwaway git worktrees at tool-internal paths like <repo>/.claude/worktrees/. These showed up as workspace rows for repos whose non-Orca worktree visibility is 'show', and would otherwise ping the discovery card and new-worktrees inbox on every fan-out. Classify them as 'agent-scratch' via a curated path-segment matcher and suppress them unconditionally: sidebar, discovery card, inbox, dialog counts, add-handoff reveal trigger, and the metadata fallback. Selected checkouts and explicitly imported paths still win. Fixes #9388 * fix: preserve scratch worktree visibility boundaries * fix: hide scratch worktrees from linked checkouts * docs: explain scratch root normalization |
||
|
|
98b00d3a64 |
refactor(comments): slim verbose comments in main core (runtime/ipc/daemon/pty) (#9542)
Collapse multi-line explanatory comment blocks into single-line "why" statements
per AGENTS.md ("Document the Why, Briefly"): drop restatements of the code and
mechanism narration; keep the non-obvious reason, external refs, and directives.
Comments-only — verified no code changed via a Babel/esbuild comment-strip
token-equality gate against origin/main; typecheck and oxlint clean.
Area: main — core runtime, ipc, daemon, pty, providers. 73 files changed, 3206 insertions(+), 10475 deletions(-).
Co-authored-by: Orca <help@stably.ai>
|
||
|
|
2e67af82d2 |
fix(worktree): bound teardown RPCs so Windows workspace deletion can't hang (#9516)
* fix(worktree): bound teardown RPCs so Windows workspace deletion can't hang Native-Windows workspace deletion failed with "Timed out waiting for physical PTY teardown". On win32 the daemon PTY adapter is the local provider, and destructive teardown's kill/listSessions RPCs used the DaemonClient 30s default — larger than the 10s sweep deadline — so a slow/wedged daemon let the outer deadline fire with the confusing error and blocked deletion. Thread an optional `timeoutMs` through IPtyProvider.shutdown/listProcesses and bound every RPC on the destructive-removal path (provider sweep, registry sweep, and the runtime-graph sweep via stopAndWait) to the remaining sweep budget minus a margin. The daemon adapter shares one budget across ensureConnected + the RPC; the SSH provider forwards the bound to the relay mux. stopAndWait splits the budget across its two sequential RPCs and bounds the cold-start wait, failing closed. Result: a wedged backend now fails fast with the accurate "Failed to physically stop every PTY" (retry succeeds once the process is truly gone), and the misleading deadline error no longer blocks deletion. Fail-closed safety is preserved: a genuinely-live process still blocks removal. Non-teardown callers pass no timeoutMs and keep the 30s default. * refactor(worktree): thread an absolute teardown deadline instead of a relative timeout Elegance pass on the teardown-RPC bounding. Instead of passing a relative `timeoutMs` and reconstructing an absolute deadline + "remaining budget" at three layers (stopAndWait, the daemon adapter's shutdown/listProcesses, and per-RPC in the sweeps), thread one absolute `deadlineMs` (epoch ms) through IPtyProvider.shutdown/listProcesses. Each RPC leaf converts to a relative timeout exactly once, at the moment it issues (`max(1, deadlineMs - now)`). Why this is cleaner: - The threaded value's identity is a point in time, not a duration, matching the codebase's existing deadline-based shared-budget idiom. - Sequential RPCs (kill then liveness verify) share the budget structurally: a later leaf converting the same deadline naturally gets less time, so the explicit recompute-after-shutdown bookkeeping disappears. - Removes the per-layer deadline re-anchoring that let the effective deadline drift slightly later at each hop. - The 500ms margin is now applied once (`teardownRpcDeadline`), and the dead `: opts.timeoutMs` else-arms in the adapter are gone. No behavior change: non-teardown callers still pass nothing and keep the 30s default + original connect behavior; fail-closed safety is intact. Tests strengthened to pin the exact leaf-observed budgets and the margin. --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> |