Upstream landed EnvironmentProviderDriver (#40315, #40332). Kept upstream's interface (EnvironmentReady.result)
and added the hosted-only extensions as optional members (renewLease, rollover, leaseExpiresAt, egressInjection,
entitlement_denied, the entitlement/renew/rollover phases); ported the hosted driver, registry and recipe adapter
to result. #40313 moved agent-catalog: fixed the #40228/#40303 imports.
* refactor: group agent launch, status, and messaging owners
* refactor: consolidate direct owner imports
* refactor: import pane ownership directly in status listeners
* 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
* feat(release): publish a server-only Linux tarball for orca serve
Each release now also uploads orca-server-linux-x64.tar.gz and
orca-server-linux-arm64.tar.gz (orcad slot from the qualified template, the
pinned Node, and an orca-ide launcher) plus orca-server-sha256sums.txt, so a
Linux host can run orca serve without the desktop package.
The tarballs are packed and smoke-tested (serve --recipe-json, then pair a
second profile) on both architectures, including bare ubuntu:24.04 and
debian:12-slim containers. Upload runs only after the release is published
and nothing depends on it, so it cannot hold the desktop release.
* fix(release): keep the server tarball's launcher env out of its terminals; skip refs that predate it
- The launcher runs the CLI with ELECTRON_RUN_AS_NODE=1, as the packaged app does, and the CLI strips it
before starting orcad; ORCA_APP_EXECUTABLE and ORCA_SERVE_RUNTIME are cleared.
- Ship Node's LICENSE beside the runtime.
- Skip packing on release refs that predate the scripts, and upload only when both tarballs passed.
- Run the PR smoke when the CLI's serve-runtime selection changes; document service install.
onorca.dev offers an opt-in 'mirror link' (https://downloads.onorca.dev)
next to the GitHub download for when GitHub release downloads 5xx.
Upload the onorca.dev download assets there right after each stable
desktop release and each Android release, instead of polling.
- config/scripts/release/publication/mirror-release-to-r2.sh: retrying,
resumable download from GitHub, size + sha256 check, S3 multipart
upload to R2 (no 300 MiB wrangler cap)
- release-mirror-r2.yml: reusable + manual backfill
- release-cut: run after publish-release for stable vX.Y.Z tags only
- mobile-android-release: run before bump-apk-download so the site's
mirror link never points at a missing APK (success not required)
* refactor: move atomic publication to its filesystem owner
* refactor: group shared browser definitions by feature
* refactor: bind proxy process and session contracts to defining owners
* refactor: consolidate direct session contract type imports
* refactor: retain atomic publication test timing under moved owner
* fix(benchmark): resolve historical tunnel framing source before loading
* refactor: separate execution route and child exit owners
* refactor: move browser data into defining feature owners
* refactor: give runtime RPC entry its shutdown implementation
* refactor: use defining process types and colocate host telemetry test
* docs(process-host): point piped child contract to Node owner
* docs(process-host): clarify both piped contract owners
- 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.
* refactor: use concrete browser and input owners
* refactor: bind remote runtime callers to concrete transport owners
* Use checked subscription mock argument types
* Type browser lease mock with its subscription contract
* refactor: bind sleeping VM status probe test to socket owner
ORCAD_CHILD_ENTRY_POINTS builds session-search-worker-entry.js, but
ORCAD_ARTIFACTS never listed it, so templates, the materializer, and the
remote installer dropped it and SSH-hosted session search failed with
'Session search worker entry not found'. Add the artifact and a test that
every built child entry is listed.
* Remove low-value ACP, hook and renderer test suites
* Remove low-value shared automation and agent test cases
* Remove low-value packaging and mobile probe test cases
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* Move session search SQLite into dedicated workers
* test: spawn the session search test worker with the shared worker helper
* fix: pass only drain promises to the channel's drain wait
* Repair session search failure and lifecycle boundaries
* Stop indexing tool output and reclaim existing rows
* test: say why the session search worker tests need a longer timeout
* Bound search cleanup and preserve failure and digest semantics
* Use the database row type for tool purge batches
* test: drop the same-connection mid-page write case now that each page reads one snapshot
* test: prove a mid-page delete from the writer can't change that page's hits
* Repair transcript cancellation cleanup and search test runtime admission
* fix: cancel active transcript finalization and enrichment waits
* fix: close transcript handles when cancellation wins delivery
* fix: preserve scan cancellation errors across parser waits
* test: prove cold session search bypasses an active writer batch
* fix: preserve search continuation through background tool cleanup
* fix: preserve search pages during orphan reclamation
* fix: bound typo repair posting visibility probes
* fix: preserve exact search terms when visibility is inconclusive
* test: drop orphan-reclaim cursor staleness case superseded by frozen continuation
* Bound session search work before ranking candidates
* Preserve the limit notice for empty search windows
* Bound session eligibility lookups and reuse FTS scoring statistics
* Index session scopes for population and candidate searches
* Derive search status from the live index after clear
* Preserve disabled search status during settings transitions
* Share planned expressions between spelling probes and search
* Keep identifier fragments out of spelling presence checks
* Report search initialization through its pending transition
* Bound spelling vocabulary work with the search posting budget
* Preserve search fallback while bounding distinct vocabulary seeks
* fix(session-search): drain removals before the worker queue fills; keep aborted close waits from failing teardown
* fix(session-search): hold writer commands when the worker queue is full instead of stopping the writer
* chore(ratchets): baseline the session search worker's one-shot entry check
* refactor: organize browser guest lifecycle owners
* test: resolve cross-version client owners and pre-ticket baseline
* test: record the released terminal registry path
* test(perf): render and identity contracts for agent status, sidebar, tab strip and chat
Twelve operation, render and identity contracts at the production seams of
the hottest UI paths, each driven at N and 10N with one hardcoded bound and
shown to fail with its regression restored. Registers them in the contract
list and fixes the perf workflow's path filter, which had drifted to 12 of
19 contracts. Removes test-only production exports nothing reads.
* test(perf): prove composer typing lands; drop dead rebuild counter
Review: the composer contract never checked the draft reached the composer,
and its transcript control could not fail; the live-index rebuild counter
and a bucket constant had no readers after the test exports went.
* chore(ratchets): count sync fs calls on Electron main and setInterval calls
Sync fs on main and fixed-cadence intervals that never settle were two of the
most common perf fix classes since September, and no gate counted them. Both
ratchets grandfather today's uses per file and fail when a file's count grows.
* chore(ratchets): count every sync fs export, src/shared and node:timers intervals
Review: the allow-list missed descriptor/copy APIs, src/shared helpers run on
main too, and setInterval imported from node:timers escaped the timer count.
* chore(ratchets): keep hosted setInterval calls beside a named timer import
Review: a named node:timers import zeroed window/globalThis calls too, a
destructured require was counted twice, and collector edits didn't select
the timer ratchet in check:changed.
* chore(ratchets): charge calls through an alias, not the alias binding
Decider ruling B on review finding C6: binding a plain alias of a gated call
counted as a use, so an alias refactor failed every collector-based ratchet.
Conditional/fallback aliases and exported bindings stay charged.
* chore(ratchets): count bare setInterval only when nothing local shadows it
Review: the timer ratchet kept its own narrower binding resolver, so alias
and namespace-destructure forms of an imported setInterval counted twice.
Module bindings are the shared collector's; a bare call is the global only
when the file declares no setInterval binding.
* chore(ratchets): resolve bare setInterval calls per scope
Decider ruling A: the file-wide shadow rule let an unrelated parameter or type
declaration named setInterval hide every global poll in the file. Each bare
call now resolves through a single-file TypeScript checker (noLib, noResolve)
and counts when it binds to nothing declared in the file.
* chore(ratchets): write the timer baseline exactly as --prune does
* chore(ratchets): charge global timer aliases and bracket access
CodeRabbit: `const schedule = setInterval; schedule(poll)` and
`window['setInterval'](poll)` counted zero. Count value references to the
global timer (per scope) instead of only direct calls.
* chore(ratchets): charge shorthand timer escapes; skip writes, typeof and JSX tags
Review: `{ setInterval }` of the global counted 0, while assignments to it,
parenthesized typeof probes and a <setInterval/> tag counted 1.
* chore(ratchets): charge compound assignments to the global timer
Review: `||=` and `??=` read setInterval before writing it, so only a
plain `=` target is write-only.
* chore(ratchets): regenerate baselines for files main moved
main's SSH reorganisation moved counted files under src/main/ssh and
src/main/managed-server; counts are unchanged except one sync fs call main
removed.
* refactor(main): organize SSH transport and managed server owners
* Remove orphan deploy readiness fixture and shrink test baselines
* refactor(wsl): import lock records from their defining owner
* refactor(wsl): use the canonical deferred update contract
* fix(github): stop PR pages from hanging when the rate-limit cache expires
Slot holders await assertRateLimitBudget -> getRateLimit while holding a
gh semaphore slot, and the /rate_limit probe queued for a slot behind
them. With all four slots held by budget waiters nothing ever released.
The probe is quota-exempt and deduped, so it no longer takes a slot.
* fix(github): bound rate-limit probe concurrency without the gh semaphore
Forced getRateLimit calls now join an in-flight probe, and all /rate_limit
probes share a small probe-only limiter that acquires nothing else.
Fold the deadlock regression test into rate-limit.test.ts as a direct await.
* fix(github): make forced rate-limit probes read quota after the call
Forced callers (403 reset refinement) share one follow-up probe queued
behind the running one instead of joining a probe that may predate the
403, so a stale positive budget cannot clear a fresh breaker block.
* fix(github): hand forced rate-limit follow-ups off synchronously
A pending forced follow-up could outlive its probe's settlement, so a
later forced caller joined a probe that predated it. Settlement now
detaches the shared deferred follow-up and starts its probe in one
synchronous step. Run rate-limit.test.ts on the Node runtime, where the
V8 scheduling that opens the race is reproducible.