Skip to content

v0.9.24: computer use and browser fixes, mothership tool deadlines and run cleanup, billing and seo improvements - #9099

Merged
waleedlatif1 merged 41 commits into
mainfrom
staging
Oct 16, 2026
Merged

waleedlatif1 merged 41 commits into
mainfrom
staging

Conversation

@waleedlatif1

@waleedlatif1 waleedlatif1 commented Oct 16, 2026 •

Copy link
Copy Markdown
Collaborator

icecrasher321 and others added 18 commits October 15, 2026 10:55
…lish (#9083)

Co-authored-by: Sim Pi Agent <pi@sim.ai>
…26 (#9082)

* feat(library): 6 Best AI Agent Options for Ecommerce Operations in 2026

* Pi Babysit: address PR #9082 feedback

---------

Co-authored-by: Sim Pi Agent <pi@sim.ai>
…n in 2026 (#9081)

* feat(library): 5 Best Discord AI Bot Builders for Community Automation in 2026

* Pi Babysit: address PR #9081 feedback

---------

Co-authored-by: Sim Pi Agent <pi@sim.ai>
Models carry observe: {} over from click and type to set_checked,
select_option, click_at, insert_text and drag, and the driver refused those
calls outright, costing a round each time (26 refusals across 650 benchmark
episodes). The observation runs after the action exactly as it does for the
other actions.
…yling (#9079)

* docs(changelog): prepare Connect agents to Oracle Database

* improvement(changelog): expand release coverage and add link icons

* fix(changelog): explain Vanta credential migration

* improvement(content): simplify links and align markdown colors
…ts stop stalling commits (#9087)

Vacuuming these tables writes WAL, mostly full-page images, faster than the
synchronous standbys flush it, so every write commit on the primary waits for
a standby until the vacuum ends. A per-table autovacuum cost delay and limit,
on each table and its TOAST relation, cap that rate.

The ALTERs are catalog-only under SHARE UPDATE EXCLUSIVE: no rewrite, and
reads and writes never wait on them, even while one is queued behind a
running vacuum. An integration test applies the migration and checks the lock
mode, that writes proceed, and that the settings land on both relations.
… declare accepted kinds (#9086)

The outbound receiver parsed a poll batch strictly, so one control kind
this build did not know threw for the whole batch: nothing in it was
answered and the worker waited out each request's timeout. Parse each
request alone, answer an unknown kind 501 sim_control_unsupported and a
malformed one 400, and keep running its batch-mates. Every poll now
declares the kinds this build's generated contract parses, so the worker
can stop sending kinds a receiver cannot handle.
* fix(desktop): make computer use act on the app the user sees

- Hit-test coordinate input with Accessibility: Notification Center and the Dock keep
  full-screen click-through windows, so the window-list check rejected every click
- Count an AX press that times out while a sheet or modal opens as dispatched
- Keep the snapshot when nothing reached the app, in the helper and the service
- Type into any focusable element (terminals, combo boxes), send keys to sheets
  and popovers, accept common key aliases
- Complete menu presses that only highlight their item, observe open menus and
  submenus first, and show menu shortcuts in labels
- Fit the model projection under Mothership's computer result budget

* test(desktop): computer-use snapshots survive actions that sent nothing

* fix(desktop): refuse unverifiable clicks, stop menu completion on cancel, scope press_key

- Refuse coordinate input when Accessibility cannot say which app owns the point
- Check cancellation before the menu-completion Return
- press_key goes to the observed window or a dialog, panel or popover above it,
  never another document window
- Only presses treat an Accessibility timeout as delivered; a value or selection
  change that times out fails as uncertain

* fix(desktop): accept press_key without a window and screenshot shorthand on actions

Mirrors the worker contract: press_key may omit windowId (the key goes to the
focused observed window, or a dialog above it), and mutations accept
includeScreenshot as shorthand for observeAfter.includeScreenshot.

* fix(desktop): type the computer-use params omit over the whole input union

* fix(desktop): confirm the click window and the highlighted menu item before input

- A coordinate click must land in the observed window, or in a sheet, popover,
  panel or menu above it; another document window of the same app is refused
- The menu-completion Return is sent only when the pressed item reports itself
  highlighted

* fix(desktop): refuse input when the receiving window cannot be confirmed

Clicks and keys go only to the observed window, or to one of the app's dialogs or
floating panels above it. A missing window attribute is allowed only for menus, and
an unreadable subrole is refused rather than treated as auxiliary.

* fix(desktop): revalidate the editor's window before every keystroke

type_text and input_sequence check, before each key, that the editor still sits in
an observed window (or a dialog above it) at its observed frame.

* fix(desktop): compare the observed window's identity, not only its bounds

Each observed windowId keeps its Accessibility window, and click, key and editor
checks require that exact window at its observed frame (or a dialog or floating
panel), so a second document window with identical bounds is refused.

* fix(desktop): refuse input to windows whose identity is ambiguous

Window-list entries match their Accessibility windows only by bounds; when two
windows share bounds neither gets an identity, so input aimed at either is refused
instead of possibly reaching the other.

* fix(desktop): read editor focus after activateFirst brings the app forward
…wn 404s (#9088)

* improvement(seo): list the v2 API as a catalog item and answer markdown 404s

* fix(seo): keep the HTML 404 for clients that refuse Markdown and answer every method
…emoved (#9089)

* fix(mothership): stop live runs when their chat, owner or access is removed

Deleting a chat, an account or a workspace, or removing someone's access,
left the worker's run going: no path issued a Stop, and run control answered
the missing chat or owner with a 404/403 the worker can only retry.

- Run control answers Stop when the run's chat is deleted, its owner or
  workspace is gone, or the owner lost access; only a failure to read is an error
- Chat delete, copilot chat delete, account deletion, workspace archive,
  member removal, organization removal, transfer and external-collaborator
  removal record Stop in their own transaction and push it after commit
- Stop delivery is shared with the user's Stop
- A takeover never claims a run whose chat was deleted

* fix(mothership): deliver removal stops from callers outside page graphs

Membership and workspace lifecycle modules sit in page server graphs, so they return the runs they stopped and their callers deliver the Stop. Fixes the organization run-control scope narrowing and shrinks the baselines this change paid down.

* fix(mothership): keep stop delivery complete when its rows or marker are gone

- Read a stopped run's client workflows and sandbox commands inside the removal's transaction, before a hard delete cascades them away
- Abort this process's controller even when the abort marker cannot be written, and keep delivering the rest of the Stop
- Account deletion no longer depends on the run-routing lookup
- The operator archive repair keeps the delivery out of its script graph
- Race test orders admission and deletion with a row-lock barrier; delivery assertions only count the test's own runs

* fix(mothership): signal a stopped run before reading its work, and deliver each removal stop once

- The user's Stop signals the worker and controller while its workflow and sandbox ownership is still being read
- Workspace deletion starts delivering its Stop as soon as the archive commits, before the slower post-archive cleanup
- External-collaborator removal delivers the Stop it recorded even when no access was left to remove
- Leaving a workspace that also ends the organization membership delivers each run's Stop once

* fix(mothership): never answer Stop for a refused delegation or an admin who keeps access

- Run control rethrows a refusal of the caller's own delegation (expired mid-request or out of scope) instead of answering Stop; only the chat, owner or access standing answers Stop
- Withdrawing a workspace grant stops the user's runs only when no effective access remains, so an organization admin keeps their run

* fix(mothership): stop an organization admin's runs when a demotion ends their access

Withdrawing a workspace grant and demoting an organization admin both end access only where no effective access remains, so the check runs after either: a role change that drops admin stops the member's runs in the organization's workspaces they can no longer reach, whatever order a directory sync withdraws the grant and the role in.

* fix(mothership): keep stopped-run identities out of removal results, check access only where runs live

- Organization member removal delivers its stopped runs and strips them from the result it returns to the route and audit
- Withdrawing a grant or demoting an admin checks remaining access only in workspaces where the user has a live run, instead of every organization workspace under the mutation lock
…me names (#9090)

* fix(mothership): bound every handed tool call by the deadline its frame names

* fix(mothership): start nothing for a tool call once its deadline has passed

* test(mothership): type the client deadline waiter mock

* fix(mothership): report a past-deadline client call as not run only when nothing claimed it

* fix(media): submit no fal request for a caller that already gave up

* fix(media): flag a refused fal cancellation and stop an aborted inline download
…it (#9091)

* fix(billing): re-read the usage gate often once a payer nears its limit

The usage gate cached a within-limit admission for five minutes, so a payer
who crossed the limit kept running executions and copilot steps for up to
that long. On a free plan that is unbilled spend; on a capped plan it bills
past the cap. An admission is now held for 10s when the payer or the acting
member has less headroom than the larger of $25 and 20% of the limit (every
free-plan payer), and for the full five minutes otherwise. The direct-v1
account verdict cache follows the same rule.

* test(billing): drive the gate-cache clock with faked timers instead of real waits
…adline left (#9092)

* fix(mothership): start no tool call with less than a minute of its deadline left

* fix(mothership): refuse a late claim at the database and settle an unclaimed call as not run
…ed sheets (#9093)

* fix(desktop): accept coordinate input without a window, one-axis scrolls and targeted keys

Mirrors the worker contract. Coordinate clicks, scrolls and drags without a windowId
target the window the snapshot selected; scroll deltas default to 0; press_key with an
elementId focuses that element first. Each shape was rejected before reaching the app
in benchmark runs (about one call per task).

* fix(desktop): let input reach sheets attached to the observed window

Go to Folder and out-of-process open and save panels are sheets: the app reports
them as its focused window, and a panel's contents report no window at all. Input
checks now treat a sheet as the window it is attached to, and a click on windowless
panel content is allowed only inside the app's focused sheet.

* fix(desktop): keep coordinate input on the selected window and confirm targeted focus

- Coordinate clicks and scrolls without a windowId use the snapshot's selected
  window through every input check; element targets keep their own window
- press_key with an elementId sends the key only once that element holds focus
- The service observes the selected window after a call without one
- A scroll needs at least one delta, matching the worker contract

* fix(desktop): observe the focused window after an untargeted key
…rovenance withheld (#9094)

* fix(billing): answer an in-flight retry at once, and tell Chat what provenance withheld

A cost callback retried while an earlier attempt of the same request was
still committing (a commit can wait on a synchronous standby while holding
the request's ledger lock) waited out lock_timeout on a pooled connection and
failed with a 500. The ledger lock is now taken without waiting: a held lock
answers 503 BILLING_REQUEST_IN_FLIGHT (Retry-After) at once, and the caller
resends later; the monotonic top-up applies the higher cumulative once.

Provenance refusals are policy, not faults: each gate admission now reports
its refusals as one warning with counts and a bounded sample instead of one
error per item, and the table envelope's unknown-row line is a warning.

Chat's CLI result now notes how many items of each kind a gate withheld, on
stderr, so the model can tell a withheld item from an absent one. Callers
outside Chat never classify content and see no change.

Also corrects two comments: the worker's callback timeout is 15 s, and no
transaction setting bounds a commit waiting on a standby.

* test(billing): model the ledger try-lock in the unit tx mock; pass the route context

The usage-log unit mock answered every statement with undefined, which the
try-lock cannot read; it now answers whether the lock was taken, with a test
that a held lock refuses before any read or write. The new route test passes
the route context like the typed handler expects, and the refusal reason type
stays module-private.

* fix(mothership): word the withheld note for every refusal, and describe the rows that read as unknown

A gate refuses either the item (unknown provenance) or only the requested
view (an excerpt or raw bytes redaction cannot cover), so the note no longer
says another read cannot return it. Untracked legacy rows read as exact-empty;
only tracked rows with a stale, missing or malformed sidecar read as unknown.

* fix(provenance): keep refused ids paired with their cause, name sandbox files by hash, and tell Chat why each item was withheld

Refusal reports are grouped by surface, cause and owning record (document,
table or chat), each line with top-level surface, cause and refusedCount and
the refused ids (up to 200, flagged when truncated), so per-cause queries and
id-to-cause pairing survive aggregation. A sandbox file is attributed by a
chat-scoped hash of its path rather than the path, which carries the user's
file name. A streamed sandbox image declares its view opaque, so its refusal
reads as opaque content rather than a missing registry; the use case now
requires every caller to declare the view.

Gate verdicts carry the refusal reason, and Chat's CLI note says per reason
what a read can do next: unknown provenance will not return on a retry,
recorded secrets can be read as text for a redacted copy, and provenance that
could not be verified may be readable later. Chunk write refusals name their
document as the parent.

* fix(billing): an in-flight refusal is not a slow ledger transaction

The usage ledger's slow-transaction warning fired for every in-flight
refusal (stage lock, not succeeded), although nothing waited and the route
already logs the refusal once at INFO. It now skips refusals. The span keeps
the existing internal_error outcome, since the closed outcome set is
generated from the legacy contracts; the response code and the INFO line tell
it apart.

* fix(provenance): keep this change to the refusal logs and note it introduced

Drops the sandbox attribution id, the streamed-image view and the owning-record
ids from this change; they belong to the provenance owner's work and fix
behavior that predates it. Refusal lines stay one per surface and cause with
top-level surface, cause and refusedCount and every refused id as each line
names it today, and the note keeps its per-reason wording.

* fix(provenance): give every refusal line a refusedCount, and say what tells an in-flight 503 apart

A single refusal now carries refusedCount 1 like a grouped one, so summing
refusedCount by cause covers both. The route comment now says the span is the
same as the retryable settlement 503's and that the body code and the INFO
line distinguish them.
…fy competitor pricing, and rebuild the GitHub, Shopify, and Snowflake integration pages (#9098)

* improvement(seo): lead comparison pages with a direct answer, re-verify competitor pricing, and rebuild the GitHub, Shopify, and Snowflake integration pages

* fix(seo): scope Sim run hosting, fetch PR files in the GitHub example, and drop stale Gumloop free-plan rows

* fix(seo): note Gumloop VPC runs are not billed for compute
@vercel

vercel Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated
docs Skipped Skipped Oct 16, 2026 6:48am UTC

Request Review

@github-actions github-actions Bot added the requires-mothership-merge Has a companion PR on the mothership/copilot side — merge in lockstep label Oct 16, 2026
@github-actions

github-actions Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

⚠️ Cross-repo companion check

One or more companion PRs aren't merged into main yet (aggregated across the feature PRs in this release). Merging this without them will leave copilot and sim out of sync — merge them in lockstep.

  • ⚠️ simstudioai/mothership#705 — merged into staging (this PR targets main) — fix(worker): leave an unknown billing outcome to replay, count only replayable l
  • ⚠️ simstudioai/mothership#689 — merged into staging (this PR targets main) — feat(memory): add personal, project, and workspace scopes

* feat(memory): authorize scoped access and durable cleanup

* refactor(memory): distinguish scoped note access controls

Rename the scoped-note control kind from memory_access to
memory_notes_access so it is distinct from graph memory_scope. The HTTP
path, delegation audience sim:memory-access, and semantic operation id
are unchanged.

* chore(audits): exempt Mothership contract copies from the relative-import rule

contracts:sync copies these files byte for byte from the worker, whose
sibling imports are relative, so they cannot use the app's aliases.
Exempt only files in lib/mothership/generated that carry the sync header,
and drop their baseline entries.

* fix(memory): read-lock note authorization, redact legacy tool calls, skip keyless cleanup

Project note checks took the Project lock exclusively while already holding
it shared, so overlapping workspace checks could deadlock; they now read
under the shared Project lock and the permission-group read lock, as
Project Files does. Exports also redact remember calls stored in the
legacy top-level toolCalls list. A Sim without COPILOT_API_KEY never
reached a worker, so it queues no memory cleanup it could not deliver.

* test(memory): use the shared env and outbox mocks for cleanup enqueue
@greptile-apps

greptile-apps Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 4/5

[High impact] Billing system changes and API contract updates across multiple services.

Desktop deadline enforcement should survive controller loss before this PR merges.

Findings

  1. P1 Desktop calls outlive their deadline ▶
  2. P2 Shopify guide skips required setup ▶

Summary

This release improves desktop and browser actions, adds tool deadlines and run cleanup, tightens billing checks, and updates stored-content evidence and public content.

  • Computer actions target the app the user sees and accept common inputs.
  • Browser actions can request an observation after any page action.
  • Tool calls stop within the deadline named by their worker frame.
  • Live Chat runs stop when their chat or access is removed.
  • Usage checks refresh faster near a limit, and duplicate cost updates get a quick retry response.
  • Chat explains which content it withheld, while stored secret provenance stays consistent.
  • Fal.ai generation and media downloads stop when their caller aborts.
  • Large database tables use gentler autovacuum settings.
  • The library and changelog publish new guides and integration updates.
  • Comparison and integration pages lead with clearer answers and refreshed details.
  • Changelog links show catalog icons and use the updated prose styling.
  • The changelog publishes expanded release notes and an integration update.

Diagram

sequenceDiagram
  participant Worker
  participant Controller as Sim chat controller
  participant DB
  participant Desktop
  participant Cleanup
  Worker->>Controller: Hand off tool call with deadline
  Controller->>DB: Store executionDeadlineAt
  Desktop->>DB: Claim call
  Note over Controller: Controller process stops
  loop While desktop action runs
    Desktop->>DB: Renew lease
    DB-->>Desktop: Renewal succeeds without deadline check
  end
  Cleanup->>DB: Find expired pickup windows or leases
  DB-->>Cleanup: Renewed call is not overdue
Loading

Reviews (1) · Last reviewed commit: "improvement(seo): lead comparison pages ..." · Reviewed by Greptile

Comment thread apps/sim/lib/mothership/request/handlers/tool.ts

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review completed against the latest diff

Turn on auto-fix | Re-trigger cubic

Comment thread apps/sim/lib/organizations/members/lifecycle.ts
Comment thread apps/sim/lib/internal/file/operations.ts
Comment thread apps/sim/lib/mothership/chat/deletion.ts
Comment thread apps/sim/app/markdown-not-found/route.ts
Comment thread apps/sim/lib/compare/data/competitors/langflow.ts
Comment thread apps/desktop/native/computer-use/ComputerUse.swift Outdated
Comment thread apps/sim/next.config.ts
Comment thread apps/sim/lib/mothership/content-delivery/telemetry.ts
Comment thread apps/desktop/src/main/browser-agent/post-action-observation.ts
Comment thread apps/sim/lib/compare/data/sim.ts Outdated
Comment thread apps/sim/lib/mothership/memories/cleanup-outbox.ts Fixed
…ction commits (#9104)

* fix(scim): push Stop for runs a directory sync ends, after its transaction commits

A directory sync that withdrew a workspace grant or demoted an organization
admin recorded Stop for the user's live Chat runs but never pushed it, so the
runs ended only on the worker's next run-control read.

- Projection deltas carry the runs their withdrawals stopped; withdrawGrant and
  setOrganizationRole return them instead of dropping them
- Group writes and user updates deliver them once their transaction commits;
  the batched reconcile delivers each batch after it commits, so a rolled-back
  batch delivers nothing
- Member removal, transfer and external-collaborator removal results carry
  stoppedRuns as a required field, returned from inside the transaction

* fix(scim): judge directory stops against the pass's final access

- A reconciliation pass that demotes an admin or withdraws a grant while granting the same workspace back no longer stops the run there: access is checked once after every withdrawal and grant
- Fix the readonly skipped result that failed type-check
- Cover a committed batch delivering when a later batch rolls back, and observe rollback deliveries deterministically
…ce than members (#9109)

* fix(slack): refuse binding a Search bot to a different app or workspace than members

* fix(slack): check Search app availability on the caller's transaction
…hare, live-doc persist and cleanup edge cases (#9110)

* fix(file-search): keep a skipped build published so rendered reads stop requeueing it

A skipped publication retired its build and cleared the revision's build id,
so a compiled document whose index build skipped never matched its rendered
artifact key: every rendered read requeued it, and the worker downloaded,
compiled and skipped it again. Skipped builds now stay published with their
bound artifact key (and no text), and the artifact-current checks accept a
skipped revision. Failed revisions still requeue.

* fix(public-shares): answer a shared Project page with a foreign embed as not found

renderSimPageDocument throws ProjectPageForeignEmbedError for a Project page
that embeds another owner's file. The authenticated artifact read maps it to
not_found, but the public content and inline reads let it escape as a 500.
Map it the same way at both public render sites.

* fix(file-doc): let the workspace document persist take a full snapshot

The relay's workspace persist ran behind the proxy with the default 10 MB body
cap, so a collaborative state above ~7.5 MiB (12 MiB is allowed) was refused
with 413 while the Project persist route took it. Exclude the workspace
persist from the proxy matcher and give it the same bypassesProxy body cap,
now shared from collab-doc state, and the same off-app-host refusal.

* chore(knip): drop the stale ignore for the v2 Project file search contract

Its only export is now imported by the v2 route and the OpenAPI spec.

* fix(memory): let a rejected key spend memory cleanup attempts

A 401 from the worker waited hourly without consuming an attempt, like a
missing route, so a permanently revoked self-hosted key kept its cleanup rows
pending forever. A 401 still waits an hour but now spends an attempt, so it
dead-letters after the default attempts (about ten hours), past a worker
rollout. 404, 405 and 501 keep waiting for free.

* fix(public-shares): log expected public file share client errors as warnings

The public file share routes logged every caught error at ERROR, including
classified client failures such as a missing share (404) or a password
exchange against a non-password share (400). Log a classified 4xx as a
warning and keep ERROR for unclassified failures.

* fix(public-shares): import the error logger in the OTP route

* test(file-search): skip on a corrupt compiled document the parsers reject
…locked owner stop their run (#9114)

* fix(mothership): settle a stopped run's call as never run only while nothing started it

* fix(mothership): let a blocked owner stop their admitted run
…utes (#9113)

* fix(copilot): load workflow chat history through the windowed chat routes

* fix(copilot): scope workflow chats without a workspace through their workflow

* test(copilot): name each workflow chat fixture's workflow uniquely
…ms of a longer query (#9115)

* improvement(search): fall back to summaries holding half the terms of a longer query

* fix(search): count Jira half-term summaries by stemmed term, from a wider pool, most terms first

* improvement(search): share the half-terms threshold and convert Jira descriptions only when filtered
* docs(library): update byok-multi-model-ai-agent-builder

* Pi Babysit: address PR #9116 feedback

---------

Co-authored-by: Sim Pi Agent <pi@sim.ai>
* docs(library): update n8n-alternatives

* Pi Babysit: address PR #9119 feedback

---------

Co-authored-by: Sim Pi Agent <pi@sim.ai>
* docs(library): update langgraph-alternatives

* Pi Babysit: address PR #9118 feedback

---------

Co-authored-by: Sim Pi Agent <pi@sim.ai>
…-healthcare-legal-procurement (#9117)

Co-authored-by: Sim Pi Agent <pi@sim.ai>
…26 (#9120)

* docs(library): update best-ai-agent-platforms-for-enterprise-teams-2026

* Pi Babysit: address PR #9120 feedback

---------

Co-authored-by: Sim Pi Agent <pi@sim.ai>
… and AX failures (#9112)

* fix(desktop): tighten computer use input guards for text, tabs, menus and AX failures

* fix(desktop): keep hidden tabs out of the window list and pin typed text to its first editor
…ose holding half of them (#9121)

* improvement(search): follow Jira summaries holding every term with those holding half of them

* improvement(search): dedupe the Jira summary tiers and the all-terms page in one pass
@waleedlatif1
waleedlatif1 merged commit 4bc5084 into main Oct 16, 2026
109 checks passed

This branch was previously deployed

1 inactive deployment
Preview — adbbe18a Deployed Oct 16, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

requires-mothership-merge Has a companion PR on the mothership/copilot side — merge in lockstep

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants