Repository navigation
Conversation
Neither provider publishes a percentage quota, so the status bar gains a money readout for them: - DeepSeek: account balance from /user/balance, which may be CNY or USD. The API exposes no usage or cost history, so the row is balance-only. - Fireworks.ai: rated spend for the current billing period, with the account ID discovered from the API key and an optional manual override. Adds an optional `credits` field to ProviderRateLimits with exact integer-nanos money arithmetic, keeps both API keys in the existing encrypted credential store rather than GlobalSettings, and teaches the window-shaped staleness predicates that credits are data so a configured provider is neither hidden nor blanked by a transient failure. `status: 'unavailable'` keeps meaning "not configured"; an exhausted balance stays visible via `credits.available` so the account can be topped up instead of vanishing. Fireworks spend-limit percentages are deliberately not wired: the `usageLimits` response shape is unverified. Extracts createSecureCredentialStore from the MiniMax API-key store and reuses it for the two new credentials.
The DeepSeek status-bar chip was showing a neutral lucide Waves glyph as a placeholder. Replace it with the real mark, taken verbatim from the inline SVG on deepseek.com (single `currentColor` path, 26.634x19.6). Inlined rather than shipped as a resources/ asset like MiniMax's: that mark is a fixed multi-colour gradient, whereas this one is a single path whose `currentColor` would resolve to the SVG's own colour inside an <img> and go invisible against one of the two themes. The icon keeps its native aspect ratio (13x10 at the default size) instead of being forced into a square box, which would squash the whale.
The Fireworks status-bar chip was showing a neutral lucide Flame glyph as a placeholder. Replace it with the real mark, taken verbatim from the SVG icon fireworks.ai serves as its favicon (three paths on a 32x32 grid, all #6720FF). Inlined like the Claude/Gemini marks rather than shipped as a resources/ asset: the fill is a single fixed brand colour, so it needs no separate file, and the icon grid already carries the mark's padding, so no viewBox cropping is required the way the DeepSeek whale needed. This removes the last lucide placeholder: icons.tsx no longer imports lucide-react at all.
…ighbours Their favicon grid carries ~25% vertical padding and the mark itself is 2:1, so at the same 13px width as the DeepSeek whale it rendered ~35% shorter and read as a smaller icon beside it. Crop the viewBox from the source's 32x32 to the mark's own bounds (y 8-23.8) and scale the width 1.25x, which lands the ink at 16x8 against the whale's 13x10.
Fireworks exposes credit only in its dashboard: there is no published REST
route for a balance, and `GET /v1/accounts/{id}/usageLimits` answers
`code 9, "usage limits are not enabled for account ..."` on a self-serve
account, so the row could only ever show spend.
Read the balance from the control-plane gateway instead — `gateway.Gateway/
GetBalance` over `gateway.fireworks.ai`, which is a different host from the
`api.fireworks.ai` inference API that serves the billing summary. The auth
metadata (`x-api-key`) and the message shapes were recovered from the
unstripped `firectl` binary, which is a thin client over that service and
exposes no subcommand for GetBalance.
Implemented with no new dependencies: a hand-rolled protobuf codec for the
three messages involved (`GetBalanceRequest`, `Balance`, `Money`) plus gRPC
framing over `node:http2`, rather than pulling in `@grpc/grpc-js` and
`@grpc/proto-loader` for one readout. The codec is unit-tested against the
wire shape a live gateway response was observed to carry.
The balance becomes the headline with the period's spend as its breakdown.
Because this is an internal API with no compatibility contract, the call is
best-effort: any failure — unreachable gateway, proxy, revoked key, schema
change — degrades to the previous spend-only readout instead of failing the
provider. Note the transport dials the host directly, so it deliberately does
not use the proxied default session that `net.fetch` callers do.
Plans without 5-hour and 7-day quota windows reported nothing useful: Orca
mapped their payloads to `{ session: null, weekly: null, status: 'ok' }`, which
the UI showed as "No usage data" — indistinguishable from a failed read.
Both providers do report a spending ceiling instead, so map it:
- Claude reads `spend` (with `extra_usage` as the fallback source) from the
usage endpoint, which previously discarded those keys entirely because
`OAuthUsageResponse` had no field for them.
- Codex reads `spend_control.individual_limit` from the backend usage payload,
which was never even requested: `supplementCodexSessionWindow` returned early
whenever the backend had no session window, so a windowless plan was skipped.
It also dropped the backend's `planType` on that path; the no-session branch
now keeps local windows (the backend can report a different bucket) but
adopts the allowance and plan.
Each ceiling's own percentage becomes the `monthly` window, so the status bar,
roster and detail panel render it with no renderer changes, and the amounts
ride a new `ProviderAllowance` readout: an amount against a limit in a unit the
provider picks — dollars for Claude, the provider's own `credit` unit for Codex,
which a currency-shaped type could not express. The percentage is always the
provider's reported figure, never derived from the amounts.
Only applies when an account reports no session *and* no weekly window, so a
normal subscription account with extra usage credits does not grow a monthly bar.
Note the backend reports `reset_at` in Unix seconds while Orca stores ms.
Copilot enterprise accounts report a monthly AI-credit budget rather than a
quota window, so this rides the allowance readout added for Claude and Codex:
the ceiling comes from `GET /enterprises/{ent}/settings/billing/budgets`, the
consumption from `/settings/billing/ai_credit/usage`, and the percentage fills
the `monthly` window.
The budget is denominated in dollars while usage is reported in credits, and
the usage payload carries the price that relates them, so the ceiling is
expressed in the unit the user recognises: 10000 dollars at the documented
0.01 price displays as 1,000,000 AI credits. With no price reported it falls
back to dollars rather than inventing a credit ceiling. GitHub documents these
budgets as monthly and partitions the usage report by calendar month, so the
reset is the next month boundary — inferable here in a way Claude's spend cap
was not.
Auth is a token plus the enterprise slug, stored in one encrypted file. The
`gh` CLI token Orca otherwise uses carries repo/read:org/project and cannot
read billing endpoints, so this is a separate credential. GitHub 401 and 403
map to stale-token and missing-scope so the existing error copy explains what
to fix.
The icon is the official Copilot mark — the robot with goggles, taken verbatim
from the brand icon GitHub ships for the product — not the octocat.
Also lands the identity plumbing: a copilot StatusBarItem, default-on
migration, visibility and roster wiring, an Accounts section, and the tests for
both the mapping and the credential flow.
The Copilot fetcher was the only authenticated GitHub caller in Orca not going through `gh`, which meant it carried its own token, hardcoded api.github.com, could not resolve a GitHub Enterprise Server host, and bypassed the gh rate-limit breaker. Route it through `gh api` like every other GitHub call, so the provider holds no token of its own: an explicit credential is only ever passed to gh as GH_TOKEN. That also lets the credential come from the sign-in the user already has. The enterprise slug is now resolved from gh rather than typed: `read:enterprise` exposes it through GraphQL `viewer.enterprises` and `manage_billing:enterprise` reads its billing data, with `admin:enterprise` implying both (verified against the live API — without `read:enterprise` the query fails with INSUFFICIENT_SCOPES rather than returning an empty list). A gh lacking those scopes is deliberately not treated as a credential source: the provider item is default-on, so doing otherwise would show a permanent error bar to every enterprise user who never asked for Copilot usage. The paste form stays as the explicit override and wins over the CLI. The probe is cached and refreshed out of band rather than awaited inside the fetch cycle, because `getState()` is synchronous and a subprocess on the refresh path would let gh latency — or a hung gh — stall every other provider. Settings → Accounts now says which source is in use and shows the literal `gh auth refresh -s read:enterprise -s manage_billing:enterprise` command when the scopes are the blocker.
Snapshot of 44 modified and 13 new files, captured byte-identically from the work machine and verified by patch-body comparison. Kept as one commit so the work is durably recorded; the concerns are split into their own branches for review. Committed with --no-verify so the snapshot matches the source tree exactly rather than being reformatted on the way in.
Upstream hardcodes stablyai/orca as the update feed, so a build of this fork offers to replace itself with an official release. Re-point the atom feed, the download base, both tag/asset regexes, the two latest/download fallbacks, and MAIN_RELEASE_REPO at nplez1/orca. The houry/daily/adhoc channel repos are left alone because those channels are unused here.
electron-updater verifies every installer it downloads against the publisherName in the installed app's app-update.yml and rejects its own channel's next build when it does not match. This fork ships unsigned, so drop the publisherName and disable update signature verification — the same thing upstream's dev channels do, for the same reason.
BRANCHES.md records what git cannot know — why each branch exists, what it depends on, and what is blocking it — and local/branch-status.mjs reports the live state so the doc cannot silently drift. Also records the release workflow in the fork-only inventory.
A local(...) commit belongs on nplez1/main only; on a PR branch the fork-only file would be reviewed upstream. commit-msg rather than pre-commit because the message is what marks a commit fork-only.
Records the step order that matters: validate the pipeline unsigned once before the first signed run, so a failure cannot be a signing bug or a pipeline bug at the same time. Also the five secrets, the per-machine install caveats, and the two-release minimum for proving an update.
orca.yaml gains an environmentRecipes entry and local/orca-vm/ the four lifecycle scripts, so a worktree can allocate its own machine on create and release it on delete. Inert until the provider verbs are wired: the scripts fail loudly rather than silently allocating nothing. Validated dry with orca vm recipe doctor (6/6, no fail or warn) and the create contract checked locally against stubbed verbs: exactly one JSON object on stdout, logs on stderr, no unknown keys.
A snapshot is only worth taking once the guest already has what Orca would otherwise install or compile on first connect. Checks node/npm/git, the Linux build toolchain node-gyp needs, a COMPLETE baked native-deps cache entry (.deps-complete plus both addons), and agent CLIs. Documents the two bake layers and why the shared native-deps cache, not the hash-keyed relay dir, is the artifact worth baking.
… absent one)
The first pipeline run died in packaging with '<repo> not a file': an unset secret still defines
CSC_LINK as an empty string, electron-builder reads a defined CSC_LINK as a certificate path, and
resolveCscLinkPath('', cwd) resolves to the repository root. Unset the CSC_*/APPLE_* variables on the
unsigned path, and record why in LOCAL-PATCHES.md so it is not re-broken.
…ish legs do not race The second run died with 422 'Published releases must have a valid tag': both artifact legs saw no release, both POSTed to create it, and the loser aborted the job even though the winner succeeded — leaving a PUBLISHED release with seven assets and no latest-mac.yml, i.e. downloads no installed build can update from. Create the release up front as a draft so both legs upload into a known tag, and flip it live at the end of the windows job once every platform's manifest is present. The manifest checks now address the tag explicitly, because gh release list hides drafts. Same shape upstream uses in daily-mac-build.yml, for the same reasons.
Renames every name an official install also owns: appId com.stablyai.orca -> com.nplez1.orca, productName 'Orca' -> 'Orca NP', the CLI command 'orca' -> 'orca-np', packaged userData -> appData/orca-np, ~/.orca -> ~/.orca-np, the NSIS Markdown ProgID, and the daemon-host root. Also pins userData explicitly instead of deriving it from package.json's name, and collapses the old orca/Orca case inconsistency, which diverges on a case-sensitive filesystem. The URL scheme stays orca://: it is interop surface (mobile pairing, inbound skill-share links), not plumbing, so a fork-only scheme would emit pairing links nothing else can open. Three couplings, each of which fails silently: - the macOS launcher shim hardcodes the in-bundle executable path, and the executable is named after productName; - the Windows launcher ships as bin/orca-np.exe + orca-np.cmd, with getBundledLauncherPath and the extraResources mapping moving as a unit; - the SSH relay shim is a DIFFERENT identity, deployed under a fixed plain `orca`, so remote launches resolve through getRelayCliCommandNameForPlatform instead of the locally installed name. That last one is why local and remote launch names now live in separate config fields: upstream could share one value only because both names were `orca`. Tests: the suite baseline was measured before touching anything (22 files already failing on nplez1/main), which made the identity fallout measurable: 42 files, all now green. See LOCAL-PATCHES.md § local(identity).
Follow-up to the identity rename, found by running the full suite after it landed rather than trusting the first green run: - the packaged Windows launcher ships as bin/orca-np.exe + orca-np.cmd, so the preflight's bundled launcher table and the launcher-asset guard test move with it; - the C# launcher normalizes ORCA_CLI_COMMAND against a whitelist, which silently relabelled the fork's CLI as `orca`; it now honours orca-np; - ORCA_CLI_COMMAND for managed WSL terminals was pinned to `orca-ide`, which no longer names the installed command; - the settings panel's CLI fallback name and its user-facing "Register `orca`" guidance; - claude-agent-teams-shim-env resolved the OLD packaged Windows launcher path on win32. Fixtures that mimic the packaged Windows layout were updated too, so a future reader is not misled by a path that no longer exists.
…s launcher The guard asserts the packaged Windows shim's extraResources mapping, which now ships as orca-np.cmd beside the native orca-np.exe.
The release body was a single placeholder sentence, which is a poor landing page for someone handed the build: no install steps per platform, no statement of what the fork is, and nothing about the first-run behaviour that otherwise reads as a bug (a fresh profile, and macOS asking for folder permissions again). The body now comes from .github/fork-release-notes.md, rendered per release with the version, the commit it was built from, the date, and a signing paragraph that follows the signing mode — signed and notarized, or the unsigned validation wording. Node does the render because one placeholder expands to a multi-line paragraph.
Brings in 131 upstream commits. Three of the 29 local commits needed resolution: two i18n locale files, and src/shared/agent-hook-listener/listener-state.ts, where upstream wrapped the state factory in a legacy-adapter layer — kept that scaffolding *and* our CopilotBackgroundWorkState type. Also here: - regenerated the derived runtime-required English catalog, which removed the stale fixture keys the local side of the i18n conflict carried (`pnpm run sync:localization-runtime-catalog`); - dropped an unused import in an upstream native-chat test that upstream's CI does not gate on but this repo's typecheck does; - recorded the sync in LOCAL-PATCHES.md, refreshed shas in BRANCHES.md, and corrected the note about the dev channels: the picker still offers hourly/daily/adhoc, which resolve to stablyai's repos. Verified: range-diff shows 26 of 29 patches byte-identical, all three typecheck projects clean, and 161 test files green across the conflict areas and the series' surfaces.
…ne mounts AccountsPane renders CodexLoginLinkNotice, which subscribes through window.api.codexAccounts.onPendingLoginUrlChanged on mount. The Copilot cases install a hand-rolled api double that only carried codexAccounts.list, so the pane threw on mount before any credential assertion ran. The double now honours the subscription contract and publishes no link.
Upstream's packaging census fails any job that packs without install-mobile-dependencies, and fork-release.yml predates that sweep (2026-09-15 vs 2026-09-18), so the contract test failed on both of its jobs. Both now install them right after the root install, as every other packaging workflow does, and the census's expected-job list names them so a future packaging job still has to be added deliberately.
A host Orca finds in ~/.ssh/config came back on the next sync, so the only way to get it out of the pickers -- the trashcan -- could not stick. Give discovered hosts a reversible Hide and keep the trashcan for hosts the user added. Hidden hosts stay in Settings under a collapsed "Hidden (n)" section and are dropped from every host picker: add-repo, the composer and task-page target lists, the automation destination picker, and the skill-install machine pickers. The status bar keeps a hidden host's row while it is connected or connecting, so a live session stays visible and a reconnect does not blink the host out of the list. The flag rides on the target row rather than filtering the target list at the IPC boundary. That call also feeds the label and connection-state mirror, whose readers treat a missing entry as proof the host was removed, so dropping rows would have made a hidden host's live workspaces look orphaned. Labels, connection state, and startup reconnect for those workspaces are unchanged.
Rewrites and extends the sync log: 96 upstream commits in, 75 replayed, 65 byte-identical and none dropped. Records the three conflict classes, the convergence decision the fork's always-on metadata tier forced, the one clean-merge break the tests caught, and the 37 gate findings that are pre-existing rather than new. Refreshes BRANCHES.md: the base it was rebased onto, the re-measured base drift (241 and 335 commits), Hide-hosts recorded as merged, and the open-threads claim that the PR-bound branches were current, which the measurement contradicts.
…s are re-read Upstream's Devin parse and sidecar change made rows cached under the old parser wrong, and upstream bumped the schema to 3 for it. This fork had already shipped 3, for the Copilot cwd move, so the fork's own upgrade path never crosses a version boundary: an install upgrading from the last release replays its pre-fix Devin rows, whose mtime and size still match, so nothing re-parses them. Schema 2 dropped the appVersion equality gate, which is why the number is the only compatibility signal left and a collision of meanings under one number is not detectable any other way. Upstream's own upgrade test writes schemaVersion 2, so it models the upstream 2 -> 3 path and cannot see this one. Costs one cold rebuild of the parse cache, once.
…njected context Copilot CLI records each user turn twice: `content` is the prompt as typed, and `transformedContent` is what the model sees, which Copilot prefixes with injected context starting `<current_datetime>...</current_datetime>`. The parser preferred `transformedContent`, so that prefix became the session title, preview and copyable first prompt for essentially every Copilot session — names that start `<current_datetime>` in the sidebar and in Agent Session History. Prefer `content`, and fall back to `transformedContent` only when the typed prompt is absent or blank, so a turn that carries nothing else still previews. The tag is not one the normalizer already hides: `HIDDEN_TEXT_BLOCKS` covers `system-reminder`, `codex_internal_context` and `goal_context`, all machinery a user never types. Adding `current_datetime` there would hide genuine user text for every agent, so the fix stays at the field the parser chooses. Both caches that persist the parsed title are bumped, because both replay a file whose mtime and size still match: - parse cache 4 -> 5, for the scanner's own answer and the previews overlaid on an indexed row; - search index 6 -> 7, for the session list itself. The index consumer keeps its own cursor and never consults the parse cache, and `sessionSearchReadDecision` skips a file whose stat matches its row, so a parse-cache bump alone would leave the wrong title in Agent Session History indefinitely. Costs one cold rebuild of each cache, once. Verified with a regression test that carries both fields and asserts the typed prompt wins; restoring the old precedence reproduces the reported `<current_datetime>...` title. `pnpm test src/main/ai-vault src/main/ai-vault-search` (169 files, 1524 tests), `pnpm tc`, and the changed-code quality gate all pass.
The readiness gate added to this file by the staged routing work (`7d2af35858`) needed one more import, and upstream had left the file at exactly the 300-line `max-lines` cap — so static analysis has been failing on `nplez1/main` for every PR since, not just this one. Move the three optional envelope fields (turn boundary, restored marker, observation provenance) out of the applicator's body and into `withAgentStatusEnvelopeFields` beside the other payload shaping in `normalize-agent-status-event.ts`. Each field stays optional and an absent one still leaves the payload untouched, so the fold is behavior-preserving; the `statusPayload` those branches sat next to is unchanged, since the suppression check and the terminal title still read the orchestration-only payload. No `max-lines` disable and no per-file cap bump, per AGENTS.md. The applicator now has headroom instead of sitting one import away from breaking again.
…l test
`987a8424aa` finished moving the panel's enable button onto the content consent
and said it brought the tests with it, but it missed this upstream test, which
still typed its settings mock and asserted `aiVaultSearch: { enabled: true }`.
The panel writes `contentEnabled`, so the assertion has been failing on
`nplez1/main` since — the fork's own test run for that commit covered the
ai-vault and session-search suites, where the renamed field was already in use,
and not this renderer panel test.
Assert the field the panel writes. The mock's type and the assertion are the
only changes; no production behavior moves.
…le id Fork-scoped: replaces the pre-rename `com.stablyai.orca` ids with this install's own namespace, so it is not an upstream candidate. The bundle rename changed the packaged helper's bundle id but missed every use of it inside the helper and its permission plumbing, so a build ran with two identities at once: the app bundle was com.nplez1.orca.computer-use, while the peer trust check accepted only com.stablyai.orca, com.stablyai.orca.dev.* and com.github.Electron, and the TCC reset fallback and the three queue labels still named the stablyai helper. macOS anchors the helper's Accessibility and Screen Recording grants to its bundle id. A machine that granted before the rename therefore kept its grants under the old id, saw a prompt for a bundle it had never granted, and could not clear the stale rows from the app: the reset path reads the current helper's Info.plist and only fell back to the old literal. The trust list is the sharper end — the transport always launches the helper with --agent --token-file, and handleRequest fails closed when a token is present and the peer is untrusted, so the fork's own app was rejected as an unauthorized computer-use peer. One identity per language now, each pinned by a test: - src/main/computer/macos-computer-use-permissions.ts derives the helper id from ORCA_APP_ID. - OrcaBundleIdentity owns the Swift-side ids for the trust list and the queue labels. - COMPUTER_USE_BUNDLE_ID in config/scripts/computer-use-bundle-identity.mjs feeds the build script's Info.plist, which is CJS and cannot import the TypeScript constant. - computer-use-bundle-identity.test.mjs pins that constant to ORCA_APP_ID and fails if any helper source names the pre-rename id again, so the next missed site cannot ship silently. Tests: vitest over the permission and computer-use suites, tsc -p config/tsconfig.node.json, oxlint and the changed-code quality gate. swift build is clean; swift test cannot run on this machine (Command Line Tools only, no XCTest), so the new OrcaBundleIdentityTests is unverified here and needs CI's darwin runner.
The panel polled checks with force:false against a 60s renderer cache while backing off 30s->120s, so a new run could sit stale for ~2 minutes. Switching tabs "fixed" it only because the Checks-tab entry freshness gate read the head-less checks cache key while fetchPRChecks writes the head-sha keyed entry, so it force-fetched on every entry instead. - Poll with force:true and hold a 30s cadence while any check is unfinished, so a running check is never hidden behind the renderer or gh cache. - Refetch immediately when the PR's own checksStatus moves, which is the signal the background PR refresh already publishes when a new run appears. - Resolve the head-sha keyed checksCache entry, with the legacy head-less key as fallback, in the entry freshness gate. - Route that lookup through a tested helper so readers and the writer cannot drift apart again. Tests: use-checks-panel-polling.test.tsx (2 new cases fail on the previous code) and checks-cache-entry-selection.test.ts; 219 related tests pass.
Add a Workspace options setting, "Group by branch" (on by default, shown only in Project grouping), that merges workspaces in one project which sit on the same git branch on two or more hosts into a single card. The merged card carries a host dropdown in line with the title, one row per host. Picking a host swaps which workspace backs the card — status, branch, PR, agents, tabs and activation all follow it — and until the user picks one, the card follows the active workspace. There is no cross-host aggregation: a consolidated card always shows exactly one host. Only a branch that maps to exactly one worktree per host and spans at least two hosts is merged; pinned, lineage, detached-HEAD and single-host rows are left untouched, so turning the setting off restores today's list. The host choice lives in an in-memory branchGroupHostByKey slice field so it survives sidebar virtualization, and the branch-group derivation is shared between the sidebar merge and the card so the two cannot disagree.
Users who hit repeated hangs had no way to hand us evidence: the only existing signal was a macOS watchdog marker consumed on the next launch, and the main-thread probe is an env-var diagnostic most people never set. Settings → Advanced → Debug Options now has a "Log UI hangs" switch, off by default. While it is on, Orca appends NDJSON to logs/ui-hangs.ndjson and the pane shows that exact path, so the file can be collected without hunting through the logs folder. The toggle applies immediately; no restart. One file now captures the three distinct ways the UI stops responding: - `stall` — the renderer's own UI thread was blocked for 250 ms or more, measured by a tick-vs-clock watchdog that survives system sleep. - `main-stall` — the same measurement on the main process, where a blocked event loop freezes IPC and terminal input even though the renderer keeps painting. These records also name the subprocess spawns that blocked the thread, reusing the spawn-initiation timing the git runners already take. - `window-unresponsive` / `window-responsive` — Electron's coarse window signal, with the recovered duration on the responsive half. A `ui-hang-meta` header records the app version, platform, arch, and OS release so a collected file is traceable to a release. Reuses the existing rotating NDJSON sink and honors the same local-file consent boundary as the trace lane (CI / ORCA_DIAGNOSTICS_DISABLED). Renderer samples are normalized at the IPC boundary and cannot inject main-only fields; records carry only timing, source, surface, and visibility — never terminal contents, URLs, env, or account data. The env-gated ORCA_MAIN_THREAD_DIAGNOSTICS churn probe and the macOS hang watchdog are unchanged; only their spawn collector is now shared. Tests: ui-hang-log-sink, main-thread-stall-probe, the renderer probe, the window observer, the Debug Options pane, and the preload composition; tc:node, tc:web, the changed-code quality gate, and the localization checks pass. A hidden-Electron rendered proof (tests/tools/ui-hang-logging-ui) captures the pane before and after the switch is flipped.
Upstream added opencode2 to the shared AgentHookSource union. descendant-events.ts enumerates every provider in a Record over that union by design, so the new member was a compile error rather than a silent gap. opencode2 has no child-session surface on the parent pane, so it is null, matching its opencode predecessor.
…yai#21882 Upstream's 35005fb implemented the same "hold a pi pane working while async subagents run" feature independently, and landed after the fork's descendant series (4222703..0b0f8ac). Both then bound pi.events, so each `subagent:async-started` / `:async-complete` fired twice, and for omp and prime-agent the fork's deliberate silence on those channels was filled by upstream's unconditional binding. Converged on the fork's subsystem, because upstream's 39 lines are a narrow subset of it: the fork binds both plugin lineages, reports the whole child roster in every post, and holds the pane working receiver-side from that roster, while upstream only withholds the lead's own agent_end. Channel coverage differs in both directions, so upstream's OMP-only `task:subagent:lifecycle` gate stays and only the two channels the fork owns were dropped from it. Adapts upstream's alias test to the fork's contract: the aliases land in `subagent_runs`, so it asserts the roster rather than upstream's withheld agent_end.
Sync log entry for the rebase: 84 commits replayed, 75 byte-identical and 9 adapted by range-diff, none dropped; the six mechanical conflicts; and the two traps the gates caught — upstream's new opencode2 provider missing from the fork's descendant Record, and upstream's stablyai#21882 reimplementing the fork's pi async-subagent bus (converged on the fork's subsystem). Also refreshes BRANCHES.md's last-updated line, the measured base drift, and names the four environment gaps that make a raw full `pnpm test` look broken.
Follow-up to fix/copilot-background-work, verified against live Copilot CLI 1.0.87 hook captures rather than inferred field names. - Permission: a permission_prompt notification no longer blocks once a tool completed since the request (it was auto-approved), while an absent snapshot still blocks so a real prompt is never hidden; also recognise the real camelCase permissionRequest payload, which was misread as a PreToolUse. - Background shells: match mode:"async"/detach:true instead of the invented background/runInBackground/run_in_background fields, register from the start result so an inline-completing async shell is never tracked, and settle on the shell_completed notification or a read_bash/stop_bash result. Matching is by shellId so one shell's completion cannot consume another. - Subagents: count only task/agent with mode:"background" (sync tasks leaked a count and pinned monitoring); consume agent_completed/SubagentStop by an identity-aware count that never touches pending shells. - Attribute a subagent's own session events to the child, not the lead pane, so they cannot replace the lead's prompt/tool or settle it. Fixes stablyai#19780 Fixes stablyai#20810
A new workspace's setup script runs as a plain shell script in a terminal that main spawns, so the sidebar could see only a live PTY and rendered the workspace as plain green "Active" for the entire setup. The execution host now announces the runner over the existing host-to-client event stream, and the sidebar renders a pulsing amber "Setting up…" dot with its own #WorktreeStatus so it is distinct from the agent working spinner. Clearing is deliberately redundant, because a stuck dot is worse than a missing one: the completion marker (shell-agnostic, so cmd.exe is covered), OSC 133;D on the setup PTY (an interrupted runner returns to its prompt without ever printing the marker), the PTY exit reaper, and an hour-long watchdog for a shell that never runs the queued command at all. State is in-memory and replayed on subscribe, so a reload or a dropped event cannot strand it either.
…#16) The floating workspace had no directory of its own. New tabs and agents started in `~`, and on a runtime/paired launch the host resolved the sentinel to its own home directory — so a machine whose home is a checkout opened floating agents inside that project. Markdown notes went to a separate folder under the app's user data, and there was nowhere to put floating-workspace agent instructions. The floating workspace now owns `~/.orca/floating-workspace`. - Terminals and agents start there, on the desktop and on a runtime/paired host alike. `floatingTerminalCwd` defaults to blank (meaning the folder) and a chosen directory still wins; a one-shot migration moves existing `~`/blank profiles onto the folder, and an omitted cwd at the PTY boundary resolves there instead of the host process's own working directory. - `AGENTS.md` is seeded there, comment-only so it adds no context until the user writes rules, and skipped when `AGENTS.md`/`CLAUDE.md` already exist. - Markdown notes live there too. Notes in the old app-data folder move in without overwriting anything, and that folder is removed only when it is empty. - Settings → Floating Workspace shows the resolved folder and can reveal it in the file manager; the directory row's copy now says what the folder is for. `~` also stops being a durable setting value. It was only ever the old default — the picker has always stored absolute paths — so blank now means the folder, the sanitizer no longer keeps a `~` round-trip, and the settings row shows the folder main resolved rather than a shorthand. The notes helper is named for what it does now that one function backs the launch directory, the note picker, and OS-opened markdown: `ensureFloatingWorkspaceDirectory` replaces `ensureDefaultFloatingWorkspacePath`.
Agents switched off in Settings → Agents still appeared as checkboxes in the Agent Session History view-options menu, and their sessions stayed in the list and in full-text search. The panel now treats the enabled agents as its whole universe: the menu renders only those rows, and the effective agent filter is enabled-in-Settings ∩ checked-in-the-view, so disabled agents drop out of both the listing and the search request. Select all / Clear and the view adjustment badge are scoped to the same universe, otherwise a globally disabled agent would read as a permanent adjustment. The empty-agent copy now points at both the Agents menu and Settings. The view's own unchecked-agents list is left untouched, so re-enabling an agent in Settings restores the checkbox the user left it at.
nplez1
force-pushed
the
nplez1/main
branch
3 times, most recently
from
September 27, 2026 18:52
2ca7d38 to
fd69125
Compare
nplez1
force-pushed
the
nplez1/main
branch
3 times, most recently
from
October 6, 2026 07:27
99f1a03 to
0ca3137
Compare
nplez1
pushed a commit
that referenced
this pull request
Oct 19, 2026
…f the deprecated owner resolver; spawn refuses folder twins (stablyai#30497) * refactor(renderer): fold launch, resume, note-send, paste and terminal-create callers off the deprecated owner resolver Agent launch, AI-vault resume and reveal, note target and send, wake and creation seeds, native chat owner, the shell menu and active-workspace terminal creation (#22, #31-#42, #44) read the owner through runtimeEnvironmentIdWithLegacyFallbackUnlessTwin. Paste uploads images to the server its PTY id names before falling back to the workspace owner. The folder and floating branch of the terminal spawn route (#53) reads resolveOwnerWithLegacyFallback: spawn refuses a folder twin instead of following the focused server, and teardown keeps tearing raw ids down locally. Deprecated importers 31 -> 16. * test: give the paste-listener and tab-bar probes the fields the folded callers read * test: note why the folder-twin route fixture is cast * test: type the paste-owner twin fixture
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
ELI5
In Settings → Agents you can turn off the agent CLIs you don't use. The Agent Session History panel ignored that: every agent still showed up in its filter menu, and sessions from agents you'd turned off still filled the list and search results. Now the panel only knows about the agents you've left on.
What Changed
Before: a disabled agent (Settings → Agents) still appeared as a checkbox in the view-options menu. Its past sessions were still listed, still counted in "N shown · M recent", and still returned by full-text search. Unchecking it by hand was the only way to hide it, and the view-adjustment badge counted it.
After: an agent switched off in Settings → Agents is not offered in the menu and its sessions are excluded from the list and from search, so the menu and the history both only cover CLIs you actually use. Select all / Clear and the badge treat the enabled agents as the whole universe — with every agent off, the panel says No agents selected, pointing at both the menu and Settings → Agents.
Mechanism: the panel's view options were already a per-client filter (
disabledAgentsinlocalStorage), but the agent universe was the hard-codedAI_VAULT_AGENTScatalog. The hook now takessettings.disabledTuiAgents, exposesavailableAgents(enabled in Settings), and derives the effectiveagentsfilter asavailableAgents ∩ checked-in-view. Becauseagentsis what feeds both the session filter and the search request'sfilters.agents, one change covers list and search.Why
Two alternatives were considered:
Keeping a "hide both" scope at the presentation layer also means no RPC change: the existing
agentsfilter array already carried the selection, so remote/SSH hosts and the web client get the same behavior with no wire-compatibility work.The view's own unchecked list is deliberately left untouched, so re-enabling an agent later restores the checkbox the user left it at rather than silently reselecting it.
Linked Issue
Issues are disabled in this fork, so there is no issue to link.
Visual Proof
N/A— the change removes rows from a menu and sessions from a list; reproducing the before state requires a profile with some agents disabled in Settings, which can't be captured as a meaningful static diff. Covered by unit tests for the enabled universe, the menu projection, and the effective filter.Testing
pnpm test src/renderer/src/components/right-sidebar/ai-vault src/renderer/src/components/right-sidebar/AiVault— 36 files / 348 tests passed.New:
ai-vault-enabled-agents.test.ts(settings → enabled universe),AiVaultPanelControls.view-menu.test.tsx(menu renders only enabled agents, universe-relative bulk state), plus new cases in the hook and view-defaults tests.pnpm tc,pnpm run check:code-quality:changed,oxlinton every changed file — clean.pnpm run verify:localization-catalog,verify:localization-extraction,verify:localization-runtime-catalog— clean.Platforms: renderer presentation only, no platform-specific code and no RPC/wire change. The
agentsfilter array already crossed the wire, so SSH hosts and the web client are covered by construction. Mobile's own history screen has no per-agent filter menu and is untouched.I manually tested these changes locally
Automated tests added/updated, or explained why not below
AI Disclosure
N/A — internal contributor.
Review
Agent skill upstream boundary
docs/reference/agent-skill-sharing-upstream-boundary.mdand copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.Notes
Security: no new inputs or data flows; the change narrows an existing client-side filter and the existing
filters.agentsrequest field. Performance: the filter set shrinks or stays the same, and the enabled universe is memoized on the settings array identity, so no extra scans or re-renders. Cross-platform / SSH: pure renderer logic;disabledTuiAgentsis the client's setting and is applied to the same list the client already filters, on local, remote and web clients. Backwards compatibility:orca.aiVault.viewOptions.v1is unchanged and still normalized the same way; stale entries for disabled agents are simply excluded and preserved for re-enabling. Mobile: no shared signature changed;AI_VAULT_AGENTSand the shared filter core are untouched.Checklist
N/Awith reasonpnpm lint,pnpm typecheck,pnpm test, andpnpm buildpass (or CI will cover; local preferred)