config/cloud-build-defaults.json feeds an ORCA_CLOUD_BUILD_DEFAULTS define in main; packaged
builds read no shell env. ORCA_BUILD_BILLING_API_URL / ORCA_BUILD_CLOUD_SANDBOX_URL override at
build time, ORCA_BILLING_API_URL / ORCA_CLOUD_SANDBOX_URL at run time. Public endpoints only.
The browser-extension preload was a main-build Rollup input, so it could be
split into shared CJS chunks that a sandboxed preload cannot require. Build it
with the other guest preloads as a self-contained bundle, and share the
one-byte unminified output options between the preload build and sandboxed
bundles so the extension preload stays one-byte in V8.
Co-authored-by: Neil <neil@stably.ai>
Request the project on the shared issue node fields and map it in
mapRawIssueForWorkspace, so list/search rows carry the project the same
way issue detail already did. Detail now reuses the shared mapping.
Co-authored-by: Neil <neil@stably.ai>
The Codex config line scanner only treated exactly three quotes as the
multiline closer, so a value like '''printf 'ok'''' left the scanner
inside the string and later tables (e.g. [projects."..."]) were missed
when writing project trust. Match toml_parser: a run of 3-5 quotes
closes the string, with up to two quotes belonging to the content.
Co-authored-by: Neil <neil@stably.ai>
Claude reports deferred categories (MCP tools, system tools loaded on
demand) that sit outside the used total, but summarizeContextUsage
dropped the flag, so the popover listed them as window percentages next
to real usage and the rows summed to well over 100%.
Keep the deferred flag on each summary row and render deferred rows in
their own group under a divider, with token counts instead of a share of
the window and a note that they are not counted in the used total.
Fixes#39878
Co-authored-by: Neil <neil@stably.ai>
Link underlines in Native Chat, the markdown preview and the rich markdown
editor sat 2px below the baseline, so descenders (g, y, p, j) crossed them and
skip-ink broke the line into dashes. Use a 0.25em offset that scales with font
size and zoom, and override the shared renderer's underline-offset-2 in Native
Chat.
Fixes#40217
Co-authored-by: Neil <neil@stably.ai>
Claude echoes "Set model to Opus 5.5" without the "Claude" vendor prefix,
and never echoes the picker-only " alias" suffix or a raw "claude-" id
prefix. The confirmation observer required every picker-label token, so a
successful switch resolved to unknown and showed "Could not verify the
model change". Normalize the expected label before matching; context
tokens such as 1M are still required.
Fixes#40058
Co-authored-by: Neil <neil@stably.ai>
When a dashboard card has no live PTY (e.g. chat-session tabs), the agent
dialog previously showed only the "preview unavailable" notice. It now also
renders the card's stored last user/agent messages, unclamped and scrollable,
below the notice. The rendering is extracted into AgentCardLastMessages and
shared with the kanban card, which keeps its line clamps.
Fixes#40144
Co-authored-by: Neil <neil@stably.ai>
* fix(diff): persist inline / side-by-side diff view via the Default Diff View setting
The file diff and combined diff toolbars toggled inline vs side-by-side only
in local component state (plus a session-only module preference for the
combined diff), so switching worktrees or agent threads reopened the diff in
the saved inline default.
The toggle now writes the existing diffDefaultView setting, and both viewers
derive sideBySide directly from it. The per-view cache and module memory no
longer carry a separate sideBySide value.
Fixes#40241
Co-authored-by: Wooseong Kim <2222333+innocarpe@users.noreply.github.com>
* test(diff): drop the removed sideBySide field from the cached combined-diff view
---------
Co-authored-by: Neil <neil@stably.ai>
* fix(terminal): don't restart every dead saved tab when a worktree opens; log how the daemon exited
Opening a worktree mounted every saved terminal tab that had no PTY, and each
hidden mount started a fresh shell or agent. With many finished agent tabs this
started dozens of agents at once and could exhaust a host's process limit.
Saved tabs with no PTY anywhere and nothing queued to start now wait until
they are shown; the existing capped idle warm-up still admits up to four.
The main process now records the daemon's exit code or signal and names it in
the respawn log line.
Fixes#39166
* fix(daemon): forget a predecessor's exit when a replacement daemon launches
The plain-shell readiness test polled with vi.waitFor (1s default budget)
for reconcileConfig and the spawn env. Under CPU load the spawn takes over
a second and the test failed with 'expected undefined to be defined'.
Await an explicit reconcile-started signal and the spawn itself, and move
cleanup to onTestFinished so the gate is released and spies/env restored
even when a regressed spawn blocks until the test timeout.
Fixes#40078
Co-authored-by: Jerry Wu <haoyuwu@andrew.cmu.edu>
The native chat split action required the source agent-session tab to carry
a TUI agent tag, so sessions without one (e.g. untagged or non-TUI agents)
could not be split and the tour's split step stalled. Any agent-session tab
in a known group is now a valid split source.
Co-authored-by: Neil <neil@stably.ai>
The sticky External Ports header used hover:bg-accent/50, a translucent
background that let scrolled port rows show through on hover. Blend the
accent into the popover color so the hover state stays opaque.
Fixes#40056
Host session-tab snapshots rebuild native chat tabs without the
client-owned focus visit, so Cmd+J recents ranked chats last and showed
their creation time.
Fixes#39841
* feat(sidebar): replace footer Settings and Help icons with an account menu
* fix: keep account menu header on primitive spacing; move focus-read allowance
* fix(sidebar): address account menu review findings
* feat(billing): Cloudflare Worker for Stripe checkout, portal, webhooks, entitlements and usage (#7)
Test-mode-only foundation for paid Orca plans: hosted Checkout/Portal,
signed + idempotent Stripe webhooks that re-read subscriptions, a D1 ledger
for sandbox usage with overage-only Billing meter events, and entitlement
checks for the desktop and the sandbox control plane.
* feat(billing): Plan & billing in the Orca account settings and account menu (#9)
Shows the signed-in account's plan, sandbox hours used and left, and opens
hosted Stripe Checkout (Upgrade) or the Customer Portal (Manage billing) in
the browser. Main mints a short-lived billing token from Orca Cloud auth and
only opens checkout.stripe.com / billing.stripe.com URLs. Hidden until
ORCA_BILLING_API_URL is set; the account menu item is gated on the 'billing'
cloud capability and registers through a local adapter that mirrors the
upcoming account menu API.
* feat(billing): plans and prices from config, opt-in Stripe Tax, real account menu
- Worker: BILLING_PLANS JSON overrides the plan catalog (labels, display price,
hours, limits); entitlements return the plan list so the desktop shows no
hardcoded prices. STRIPE_AUTOMATIC_TAX=true adds automatic_tax and tax id
collection to Checkout (default off). Setup script takes prices from env.
- Desktop: register Plan & billing through the sidebar account menu
(merged nwparker/sidebar-account-menu) and drop the local adapter.
- Register the billing Worker's tests with the test-runner check.
* feat(billing): Team pricing, opt-in overage, Stripe test-mode e2e, dev deploy (#18)
* feat(billing): Team-only pricing, opt-in overage with spend cap, live Stripe test-mode e2e
- Catalog: Team $50/seat/mo or $480/seat/yr, 60 Standard h/seat/month pooled
(monthly windows on annual plans), Large 2x, 3 running/seat, 15-min idle sleep;
Enterprise is contact-only. All numbers/labels in BILLING_PLANS defaults.
- Overage is opt-in (adds the metered item), $0.75/h, monthly spend cap (default
$25, admin-raisable) enforced in the meter outbox; 80%/100% usage warnings.
- Checkout: plan/interval/seats with adjustable quantity; portal configuration var.
- Workers reject fetch redirect 'error': use 'manual'. Log route errors.
- scripts/stripe-test-setup.ts creates the test catalog + portal configuration;
scripts/e2e-test-mode.ts drives Checkout (headless, test card), overage, meter,
cap, annual, test-clock renewal, payment failure and cancel.
- Desktop: Team/Enterprise cards with interval + seats, usage warnings, overage
toggle + cap; split into copy/offers/usage modules.
* feat(billing): dev deployment config, dev operator keys, return page
- wrangler.dev.jsonc for orca-billing-dev (workers.dev, dev D1, test-mode prices).
- BILLING_DEV_JWKS (issuer orca-billing-dev-test) accepted only when BILLING_ENV
is not prod, for e2e runs until auth mints billing tokens.
- GET /return: static 'return to Orca' page for Checkout/Portal redirects.
- Design doc: final pricing, dev URL and resources for the control plane.
* feat(billing): team seats follow membership, payment grace + suspend signal, Team UI
Worker
- Seats = org members from Orca Cloud auth (single source of truth; pending invites
never count; min 1): auth pushes POST /v1/internal/seats, billing pulls
/v1/internal/billing/org-members at Checkout and in a reconcile cron, fails closed
(503) if auth is configured but unreachable. No token claim carries a count.
- Proration: adds always_invoice, removals create_prorations (Stripe default).
- Billing admin actions require a token minted in the last 6 minutes.
- Payment failure: past_due_since (migration 0003); new starts stop at once;
after 96 h (paymentGraceHours) reason payment_grace_expired + suspendRunning on
check/usage, and GET /v1/internal/suspensions for the control plane.
- Portal config: monthly<->annual + cancel only (seats follow membership).
- e2e: seat add/remove proration, grace expiry -> suspend -> pay -> resume.
Desktop
- Team section + account menu item: members/roles, invite, change role, remove
with confirm, revoke invites (owners/admins, auth role rules mirrored).
- Payment issue banner with suspension date and Update payment method.
* feat(billing): in-app plans page (#22)
A pricing/plans page reachable from the account menu ("Plan & billing"), the
Settings plan section ("Choose a plan" / "Compare plans"), and any upsell via
openPlansPage(). Everything comes from the billing service's offers: Team
monthly/annual (with the computed annual saving), pooled hours, Large weight,
concurrency, idle sleep, opt-in overage with spend cap, and Enterprise contact.
- States: choose (admin), locked (member), current plan with usage, payment
failure (status-first), checkout in progress, loading and failure with retry.
- Seats are read-only: one per team member (billing re-reads the count).
- Shared card shell keeps both cards' footers level; sliding interval toggle and
price fade respect reduced motion; dialog opens without a pre-focused button.
- Worker offers gain per-seat cents and the Large weight for totals.
- Copy matches the marketing /cloud promises: agents keep running, wake in
seconds, secrets never stored in the VM.
* wip(cloud): Orca Cloud built-in provider wiring
* feat(cloud): no-plan upsell opens the plans page
* feat(cloud): Orca Cloud dogfood fixes (provider persistence, picker label, any-input activity, idle override)
* fix(cloud): live dogfood fixes (snapshot already-deleted, agent egress wildcards, golden image id) + results
* fix(cloud): i18n keys, typed dev JWK, verdict typing
* feat(cloud): billing caps from billing (maxConcurrentSandboxes, per-member), userId on usage; concurrency copy
* feat(cloud): golden image can pin a released orca-server tarball (--release), source build stays the fallback
* fix(cloud): async dev key read, runtime localization catalog
* refactor: group renderer workspace ownership and navigation
* refactor: use the defining WSL rewrite store identifier
* refactor: route new upstream consumers to workspace owners
* refactor: consolidate connection owner test imports
* refactor: preserve upstream review-owner test retirement
- Seats billed = the period's peak membership: adds above paid seats are charged now
(always_invoice); removals never credit mid-period and only lower the quantity inside
seatTrueDownHours of renewal (proration none), so remove/re-add churn can't dodge or
double-charge. The cron applies deferred decreases from the last pushed count.
- Included hours and caps come from billed seats; per-member concurrency cap
(member_concurrency_limit); usage attributed to the member who started it.
- Start check returns billed `seats` and the derived caps for the control plane.
- Desktop shows when paid seats drop at renewal; proration copy updated.
- Design doc: seat-fairness decision with the GitHub/Cursor/Slack/Linear comparison.
Sign out of Claude/Codex on a host now runs that agent's logout against the
login launches there use, after a confirm dialog, instead of deleting the
host's saved Orca account. The host check asks the CLI in that same login.
Starting Claude or Codex on a runtime host where it isn't signed in holds the
launch behind an inline card. Sign in with browser runs the unmodified CLI login
on the host; this computer forwards the browser's localhost callback to it over
a narrowly scoped tunnel, so the token never leaves the host. Adds a verdict-only
host check, agent sign-out in the host menu, and paste/device-code fallbacks.
* fix(dictation): don't throw when the paired web client has no speech catalog
* feat(i18n): translate the dictation pill, error pill and queued-send copy
* fix(i18n): say Chinese queued sends happen automatically
* fix(terminal): end a dead preserved terminal host's PTYs as host loss
After an update Orca keeps the previous version's terminal host so agents
survive. That host has no respawn, so when it later died nothing ended its
PTYs: panes stayed connected on a frozen frame and input went nowhere.
Once every reconnect attempt finds its endpoint refused or missing, each of
its PTYs now ends as host loss with history kept, and the pane's restore is
no longer refused because the dead host cannot answer.
Fixes#38432
* fix(terminal): require process-exit proof on POSIX and keep checking until proven
Review follow-ups: an unproven attempt restarts the count instead of ending
the check; on POSIX the old host's pid must also be gone; a proven-gone host
answers 'not live' for every id; one throwing exit listener cannot strand
the remaining sessions.
* fix(dictation): wait as long as the host's cloud deadline when a paired phone or browser stops
* fix(dictation): compute the host's cloud deadline from its exact sample count
* fix(agent-picker): list agents named by the search before other agents' model setups
Typing "codex" in the agent picker showed OpenCode/Cursor model setups
(e.g. "OpenCode · GPT-5 Codex") above the Codex agent itself, and Enter
picked the first setup. Direct agent matches now render first and are the
Enter target.
* test(agent-picker): assert search highlight and Enter target for named agent
* test(agent-picker): import fireEvent after rebase