Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
136 commits
Select commit Hold shift + click to select a range
0b24aec
fix(sdk): bound a credential provider call so an unreachable store fa…
GeiserX Aug 14, 2026
7cc550f
fix(sdk): keep the provider's receiver when bounding its calls
GeiserX Aug 14, 2026
f9c7999
fix(sdk): stop the bounded wrapper dropping a provider's prototype me…
GeiserX Aug 14, 2026
1b5f931
Add integrations.remove to core tools (#1600)
RhysSullivan Aug 16, 2026
624e85f
Report honest removed flag from oauth.clients.remove (#1603)
RhysSullivan Aug 16, 2026
8cb0d22
Migrate outbound MCP client to SDK v2 (spec 2026-07-28)
RhysSullivan Aug 16, 2026
0b1739b
Serve MCP spec 2026-07-28 and consolidate onto SDK v2
RhysSullivan Aug 16, 2026
a9b33d2
Add changeset for MCP 2026-07-28 support (#1617)
RhysSullivan Aug 17, 2026
60ad50c
Version Packages (#1557)
RhysSullivan Aug 17, 2026
6dff891
Isolate the legacy GHCR mirror in its own release job (#1620)
RhysSullivan Aug 17, 2026
86c68af
Stamp client identity on MCP execution spans (#1621)
RhysSullivan Aug 17, 2026
19a1d21
Improve trace quality: redact headers, fix DO span parenting, cut noi…
RhysSullivan Aug 17, 2026
a8d3d3c
Sign out browsers whose session has already ended (#1624)
RhysSullivan Aug 17, 2026
f3ec48d
Answer dead-session standalone GETs with 405 to stop reconnect loops …
RhysSullivan Aug 17, 2026
dd8aab7
Pin worker logpush in wrangler config (#1625)
RhysSullivan Aug 17, 2026
d3f0617
Bind span header redaction to the hosted HTTP client (#1626)
RhysSullivan Aug 17, 2026
2ea7494
Negative-cache dead MCP session ids in the workers (#1627)
RhysSullivan Aug 17, 2026
4263f33
Warm the Start server graph on isolate first-fetch (#1628)
RhysSullivan Aug 17, 2026
003fb07
First-party OAuth clients (#1527)
RhysSullivan Aug 17, 2026
75a3c45
Keep the standalone MCP SSE listener open after undelivered replay (#…
RhysSullivan Aug 17, 2026
df2eeaa
Add Gmail modify support (#1630)
RhysSullivan Aug 17, 2026
c443219
Hold MCP listener streams open: keepalives + drained Last-Event-ID ro…
RhysSullivan Aug 17, 2026
deb39f7
Trace first-party OAuth usage (#1633)
RhysSullivan Aug 17, 2026
194ef26
Revert "Warm the Start server graph on isolate first-fetch (#1628)" (…
RhysSullivan Aug 17, 2026
f5fffae
Add Google OAuth homepage (#1635)
RhysSullivan Aug 17, 2026
77f7ac3
Route Google OAuth page to marketing (#1636)
RhysSullivan Aug 17, 2026
ffb28cc
Bypass Start for marketing requests (#1637)
RhysSullivan Aug 17, 2026
7f5151e
Add fresh Google Workspace homepage (#1638)
RhysSullivan Aug 17, 2026
c83cdd2
Add plain Executor overview (#1639)
RhysSullivan Aug 17, 2026
f64028a
Speed up and stabilize CI e2e (#1551)
RhysSullivan Aug 18, 2026
63ea028
Add Slack first-party OAuth (#1640)
RhysSullivan Aug 18, 2026
5e1875f
Show first-party OAuth for MCP discovery (#1641)
RhysSullivan Aug 18, 2026
32206c7
Preserve OAuth scopes on spec refresh (#1642)
RhysSullivan Aug 18, 2026
e2d9f23
Handle Slack user OAuth scopes (#1643)
RhysSullivan Aug 18, 2026
b18e8cf
Select Slack user token grants (#1645)
RhysSullivan Aug 18, 2026
9ecc7cb
Opt-in modern protocol negotiation for stdio MCP servers (#1646)
RhysSullivan Aug 18, 2026
86acd38
Bring CI under two minutes (#1644)
RhysSullivan Aug 18, 2026
039422c
Drop ignored package from mixed changeset (#1651)
RhysSullivan Aug 18, 2026
22a0383
Version Packages (#1653)
RhysSullivan Aug 18, 2026
12a1bb2
Normalize Slack OAuth scopes (#1652)
RhysSullivan Aug 18, 2026
256e25e
Kill stdio MCP children when a dial is interrupted (#1654)
RhysSullivan Aug 18, 2026
1c24f56
Fix two CI e2e flakes: stylesheet-count race and dev-db queue wedge (…
RhysSullivan Aug 18, 2026
34f3720
Capture pageviews on the marketing site (#1655)
RhysSullivan Aug 18, 2026
739805f
Support GitHub App OAuth permissions (#1659)
RhysSullivan Aug 18, 2026
a5f3400
Fix two more CI e2e flakes: picker hydration race and dev-db ghost af…
RhysSullivan Aug 18, 2026
41ec2d6
Poll the emulator ledger instead of racing its write (#1661)
RhysSullivan Aug 18, 2026
586a9c0
Recognize vite strictPort exits as port collisions in claimAndBoot (#…
RhysSullivan Aug 18, 2026
d16abec
Expand first-party Google Workspace support (#1663)
RhysSullivan Aug 18, 2026
06786d0
Make latency regressions attributable: version stamps, deploy markers…
RhysSullivan Aug 18, 2026
5897721
Generate the isolate instance id lazily, not in global scope (#1666)
RhysSullivan Aug 18, 2026
81d4cf9
Cache JWKS across isolates and serve stale keys on refresh failure (#…
RhysSullivan Aug 18, 2026
e27931f
Attribute local_verify latency to a phase (#1667)
RhysSullivan Aug 18, 2026
c0d25e4
Split the jwt_verify cost into store read vs key import (#1668)
RhysSullivan Aug 18, 2026
46cea2c
Add Gmail settings access
RhysSullivan Aug 18, 2026
e77e09c
Auto-merged main into fix/gmail-settings-scopes on deployment.
RhysSullivan Aug 18, 2026
58ebd70
Add temporary clock-sync probe to separate pre-work time from real wo…
RhysSullivan Aug 18, 2026
0e5cb44
Probe cache and timer latency directly in the handler (#1671)
RhysSullivan Aug 18, 2026
7277833
Fix probe typecheck: caches.default needs the Workers CacheStorage ty…
RhysSullivan Aug 18, 2026
fde4450
Probe a real outbound fetch alongside the local probes (#1673)
RhysSullivan Aug 18, 2026
68d5e8b
Split header time from body-read time on the docs upstream (#1674)
RhysSullivan Aug 18, 2026
dd0bbd2
Separate Start cold-graph cost from per-request cost (#1675)
RhysSullivan Aug 18, 2026
2670c2b
Clock-sync both sides of fetchHandler so handlerMs is real (#1676)
RhysSullivan Aug 18, 2026
9551da9
Time Start module evaluation separately from first-request app init (…
RhysSullivan Aug 18, 2026
54ccca9
Forward docs and PostHog proxies before loading the Start graph (#1678)
RhysSullivan Aug 18, 2026
42d4e49
Fix Google OAuth service policies
RhysSullivan Aug 18, 2026
44d9ed3
Merge pull request #1669 from UsefulSoftwareCo/fix/gmail-settings-scopes
RhysSullivan Aug 18, 2026
ca9617e
Warm the Start server graph on isolates that persist (#1679)
RhysSullivan Aug 18, 2026
332955f
Revert "Warm the Start server graph on isolates that persist (#1679)"…
RhysSullivan Aug 18, 2026
29a0aea
Warm the Start server graph on isolate first fetch (#1681)
RhysSullivan Aug 19, 2026
aff1f39
Revert "Warm the Start server graph on isolate first fetch (#1681)" (…
RhysSullivan Aug 19, 2026
46625d2
Warm the Start graph by replaying a real request per isolate (#1686)
RhysSullivan Aug 19, 2026
1b2509c
Log whether the warmup runs and whether warm isolates still pay for S…
RhysSullivan Aug 19, 2026
1c57887
Fix lint: do not stringify the unknown warmup error (#1688)
RhysSullivan Aug 19, 2026
1c62f4a
Remove the Start graph warmup attempts and latency probes (#1689)
RhysSullivan Aug 19, 2026
b6e8c68
Revert "Serve MCP spec 2026-07-28 and consolidate onto SDK v2" (#1611…
RhysSullivan Aug 19, 2026
98a34e3
Restore "Serve MCP spec 2026-07-28 and consolidate onto SDK v2" (#161…
RhysSullivan Aug 19, 2026
d89c733
Revert "Migrate outbound MCP client to SDK v2" (8cb0d22a3) (#1692)
RhysSullivan Aug 19, 2026
d5beb46
Drop targeted placement to test isolate distribution (#1693)
RhysSullivan Aug 19, 2026
cd9b978
Add isolate lifecycle signals to worker spans (#1694)
RhysSullivan Aug 19, 2026
54cde5e
Drop the MCP Durable Object from the SSR app graph (#1696)
RhysSullivan Aug 19, 2026
a5800a1
Revert the outbound MCP SDK v2 migration (#1697)
RhysSullivan Aug 19, 2026
4817fa1
Drop the e2e assertion contradicting the Google scope allowlist (#1700)
RhysSullivan Aug 19, 2026
4cc5c4d
Measure isolate reuse and the evaluated module closure directly (#1698)
RhysSullivan Aug 19, 2026
f08e84d
Load the code-execution stack on demand in the MCP session DO (#1699)
RhysSullivan Aug 19, 2026
9e7afaf
Disable product telemetry in CI (#1701)
RhysSullivan Aug 20, 2026
b917755
Exempt paid organizations from the execution rate-limit backstop (#1695)
RhysSullivan Aug 20, 2026
9a38ab4
Restore targeted placement to aws:us-east-1 (#1704)
RhysSullivan Aug 20, 2026
2bdbedf
Reserve the OAuth sign-in window on the click, not after discovery (#…
RhysSullivan Aug 20, 2026
e9920c3
Fix Slack Connect image reads (#1705)
RhysSullivan Aug 20, 2026
8f20477
Serve /api at the Worker entry instead of through TanStack Start (#1706)
RhysSullivan Aug 20, 2026
91ee3db
Stop CI failing on waits that were never about the product (#1707)
RhysSullivan Aug 20, 2026
dc1b5aa
Import the app handler lazily in start.ts (#1710)
RhysSullivan Aug 20, 2026
442b3f0
Build the Ajv instance on first use and mark codemode-core side-effec…
RhysSullivan Aug 20, 2026
73cbba9
Disable product telemetry in every workflow (#1709)
RhysSullivan Aug 21, 2026
c11bef2
Cloudflare: MCP-only preset with codemode=false, warn on missing opt-…
RhysSullivan Aug 21, 2026
6cad3d2
Classify sandbox execution failures on execute spans (#1726)
RhysSullivan Aug 24, 2026
9ba9e7a
Disable telemetry in terminal E2E sessions (#1728)
RhysSullivan Aug 25, 2026
6ea21bc
Disable telemetry in desktop smoke container (#1729)
RhysSullivan Aug 25, 2026
a2d1417
Keep connection list health compact by default (#1648)
baggiiiie Aug 25, 2026
0b0b74f
Reland outbound MCP client on SDK v2 (spec 2026-07-28) (#1733)
RhysSullivan Aug 25, 2026
0c81533
Stabilize cloud E2E scenarios (#1735)
RhysSullivan Aug 25, 2026
69c25d2
Fix README demo on npm (#1734)
RhysSullivan Aug 25, 2026
0557d8d
Restore the marketing hero copy (#1738)
RhysSullivan Aug 25, 2026
9296f36
Scope the skills tool description to executor's own docs (#1737)
RhysSullivan Aug 25, 2026
0ae7959
Version Packages (#1657)
RhysSullivan Aug 25, 2026
2385866
Render README demo as GIF (#1739)
RhysSullivan Aug 25, 2026
62748e8
Add opt-in per-integration MCP search tools (#1741)
RhysSullivan Aug 25, 2026
05c3b22
Drop eager MCP schema preload in workerd isolates (#1742)
RhysSullivan Aug 25, 2026
91062c2
Load the MCP client SDK lazily on first outbound connection (#1747)
RhysSullivan Aug 25, 2026
1bbd47e
Fix dev-db socket server slot leak and add a wedge watchdog (#1744)
RhysSullivan Aug 25, 2026
eb34d04
Serve the cloud console as an SPA with a prerendered shell (#1748)
RhysSullivan Aug 26, 2026
d4afe0c
Slim per-integration search tool definitions (#1749)
RhysSullivan Aug 26, 2026
b429102
Re-arm the post-checkout billing poll across effect recycles (#1750)
RhysSullivan Aug 26, 2026
0007474
Stream the OpenAPI preview for Graph-sized spec selections (#1751)
RhysSullivan Aug 26, 2026
ddbf0fe
Serve Microsoft Graph selections from precomputed spec slices (#1753)
RhysSullivan Aug 26, 2026
6574ab1
Stop evicting standalone MCP listeners; fail fast when the transport …
RhysSullivan Aug 26, 2026
7c12aee
Make Microsoft Graph slice URLs first-class spec sources (#1755)
RhysSullivan Aug 26, 2026
ea5ae61
Bump free tier to 100k executions per month (#1757)
RhysSullivan Aug 26, 2026
a788874
Label oauth2 auth methods so multiple templates stay distinguishable …
RhysSullivan Aug 26, 2026
5d18978
MCP enterprise-managed authorization (client) (#1730)
RhysSullivan Aug 26, 2026
e76a9b4
Capture executed code and result sizes in cloud telemetry (#1727)
RhysSullivan Aug 26, 2026
2c75d7a
Gate enterprise-managed authorization behind a PostHog flag (#1758)
RhysSullivan Aug 26, 2026
30cfb4d
Serve runtime-observed output shapes for schemaless tools (#1759)
RhysSullivan Aug 27, 2026
29b7a77
Connect dialog: search the integrations.sh catalog (#1763)
RhysSullivan Aug 27, 2026
4c9e755
Fix blurry YC badge on mobile (#1770)
RhysSullivan Aug 27, 2026
cd3d000
Classify non-JSON and HTTP-200 refresh refusals as dead grants (#1783)
RhysSullivan Aug 27, 2026
61b3d9a
Provision Autumn customers for orgs and self-heal customer_not_found …
RhysSullivan Aug 27, 2026
e3acef7
Classify Durable Object platform failures as retryable protocol error…
RhysSullivan Aug 27, 2026
55180cb
Shape storage error messages and classify connection faults (#1784)
RhysSullivan Aug 27, 2026
b885436
Trace and type the execution rate-limit counter (#1782)
RhysSullivan Aug 28, 2026
27b044d
MCP session cold init resilience (#1787)
RhysSullivan Aug 28, 2026
c59e23c
Classify CPU-limit Durable Object resets as retryable (#1792)
RhysSullivan Aug 28, 2026
36c901b
Stable Sentry error grouping across deploys (#1781)
RhysSullivan Aug 28, 2026
e63a6b7
Unlist the built-in Google OAuth app from connect pickers (#1795)
RhysSullivan Aug 28, 2026
3927eba
Back off vault version-check retries and persist rotated refresh toke…
RhysSullivan Aug 28, 2026
d169ee2
Merge branch 'main' into fix/bound-credential-provider-call
RhysSullivan Aug 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
20 changes: 0 additions & 20 deletions .changeset/admin-users-batched-connection-read.md

This file was deleted.

5 changes: 5 additions & 0 deletions .changeset/graph-slice-urls-first-class.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@executor-js/plugin-openapi": patch
---

Make Microsoft Graph slice URLs first-class spec sources instead of a hidden substitution. Catalog tiles now point directly at the slice release assets, the stored specUrl is exactly what gets fetched, and selection narrowing travels visibly in the URL fragment; requesting the upstream monolith URL fetches the monolith, never a silently swapped slice.
5 changes: 5 additions & 0 deletions .changeset/graph-spec-slices.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@executor-js/plugin-openapi": patch
---

Serve Microsoft Graph preset selections from precomputed slice release assets instead of the 43MB upstream monolith. The monolith fetch almost never survives a 128MB Workers isolate (production traces show one completion in 30 days), so covered selections — every catalog preset, plus any combination within the default bundle — now read a 4–19MB filtered document built offline by the graph-slices workflow, with the monolith path kept only as a fallback and for full-graph/custom-scope selections.
5 changes: 5 additions & 0 deletions .changeset/lazy-mcp-client-module.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@executor-js/plugin-mcp": patch
---

Load the MCP client SDK lazily on first outbound connection instead of at module evaluation. Runtimes that bundle the plugin (notably Cloudflare Workers) no longer pay the client package's module-eval memory and CPU on startup or on code paths that never dial an MCP server.
7 changes: 0 additions & 7 deletions .changeset/local-native-elicitation-streaming.md

This file was deleted.

8 changes: 8 additions & 0 deletions .changeset/mcp-namespace-search-tools.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@executor-js/execution": patch
"executor": patch
---

**Opt-in per-integration search tools on the MCP surface**

Connecting with `?search_tools=true` (stdio: `executor mcp --search-tools`) adds one minimally-described `search_<integration>` MCP tool per connected integration, so the integration namespaces reach the model as tool names it can see without calling anything. Each call routes through the same flow as `tools.search({ namespace })` inside `execute`, and the tool list comes from the same inventory the `execute` description shows. Off by default; a clean endpoint URL is unchanged.
5 changes: 5 additions & 0 deletions .changeset/openapi-streaming-preview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@executor-js/plugin-openapi": patch
---

Preview OpenAPI spec-format selections (Microsoft Graph) through the streaming structural-split path instead of a whole-document parse, and guard generic whole-document parses by parsed-tree size (line count for block YAML, text size for JSON). Previewing a Graph preset URL previously parsed the 43MB source whole and killed the 128MB Workers isolate mid-request, surfacing as an empty 503; it now streams within budget, and oversized generic specs fail with an actionable error instead of taking down the isolate.
6 changes: 6 additions & 0 deletions .changeset/slim-search-tools.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@executor-js/execution": patch
"executor": patch
---

Slim the per-integration `search_<integration>` tool definitions to under half their size: one shared one-line description (the tool name already carries the namespace) and a single bare `query` parameter, dropping the `limit`/`offset` knobs. A session pays for these definitions once per connected integration, so the surface now costs ~2k tokens instead of ~5k at 30 integrations; paging through a namespace belongs in `execute`.
8 changes: 8 additions & 0 deletions .changeset/storage-error-shaping.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@executor-js/sdk": patch
"@executor-js/api": patch
---

Build `StorageError.message` from the call-site label plus the driver's error code instead of the driver's raw text. The driver text is drizzle's `Failed query: <sql>\nparams: <bound values>`, so error reporting grouped one storage defect by statement shape and printed bound parameters into issue titles. The full driver error stays on `cause`.

Add `StorageConnectionError`, a `StorageFailure` variant for postgres.js connection faults (`CONNECTION_ENDED`, `CONNECTION_CLOSED`, `CONNECTION_DESTROYED`, `CONNECT_TIMEOUT`, `ECONNREFUSED`, `ECONNRESET`) and workerd's cross-request I/O rejection. It carries the fault `code` and a `retryable` flag so a lost socket can be told apart from a pool-lifetime bug.
19 changes: 18 additions & 1 deletion .claude/skills/prod-telemetry/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,20 @@ join the same traces via traceparent).

**Span names worth querying** (and their custom attrs):

- `mcp.execute` / `mcp.execute.resume` — `mcp.execute.mode`
(`pausable`/`inline`), `mcp.execute.code_length`, and
`mcp.execute.outcome` (`ok`/`fail`/`paused`) with, on failures,
`mcp.execute.error_kind` (`type_error` | `reference_error` |
`syntax_error` | `range_error` | `tool_error` | `timeout` |
`resource_limit` | `serialization_error` | `thrown` | `unknown`).
Sandbox script failures ride the MCP success channel, so `status.code`
stays OK — filter on these attributes, not span status. Spans from
before the attributes shipped carry neither; absence is not success.
Also `mcp.execute.result_chars` (compact-JSON size of the returned
value, pre-truncation; -1 = unmeasurable), `mcp.execute.log_chars`,
`mcp.execute.emitted` — the dump-vs-narrow signal (the model preview
truncates at 30k chars, so `result_chars > 30000` means the model tried
to pull a truncated blob into context).
- `executor.tool.execute` — `mcp.tool.name` (full address), and since
PR #992: `executor.tool.outcome` (`ok`/`fail`),
`executor.tool.error_code`, `executor.tool.error_status`,
Expand All @@ -39,7 +53,10 @@ join the same traces via traceparent).
`base_url`, and since PR #992 `http.status_code`.
- `mcp.request` (outer) — `mcp.auth.organization_id`,
`mcp.auth.account_id`, `mcp.tool.name`, CF edge fields (`cf.country`…),
MCP client fingerprint (`mcp.client.name`…).
MCP client fingerprint (`mcp.client.name`…), and on managed-cloud
`execute`/`execute-action` calls `mcp.execute.code` (the script itself,
capped at 10k chars — cloud-only content capture; local/self-host
telemetry never records content).

**Recipe — error signatures by class (the daily-digest query):**

Expand Down
164 changes: 107 additions & 57 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,18 @@ concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true

# CI is not a user. Without this, executor's own product telemetry reports to the
# production PostHog project from every e2e shard: the analytics layer
# (integration_added, execution_completed, artifact_*) and the
# integrations-registry fetch, which lands as a `hit` event. Each shard is a
# fresh container, so it mints a fresh anonymous id and arrives on a fresh
# runner IP — inflating machine and user counts by roughly an order of
# magnitude. Both opt-outs are the packages' documented CI/test hooks.
env:
DO_NOT_TRACK: "1"
EXECUTOR_DISABLE_ANALYTICS: "1"
EXECUTOR_DISABLE_INTEGRATIONS_FETCH: "1"

jobs:
changes:
name: Changed paths
Expand All @@ -18,6 +30,7 @@ jobs:
outputs:
desktop_smoke: ${{ github.event_name != 'pull_request' || steps.filter.outputs.desktop_smoke == 'true' }}
selfhost_docker_smoke: ${{ github.event_name != 'pull_request' || steps.filter.outputs.selfhost_docker_smoke == 'true' }}
cloud_closure: ${{ github.event_name != 'pull_request' || steps.filter.outputs.cloud_closure == 'true' }}
steps:
- uses: actions/checkout@v4

Expand All @@ -41,6 +54,14 @@ jobs:
- "packages/kernel/runtime-quickjs/**"
- "packages/plugins/**"
- "packages/react/**"
cloud_closure:
- ".github/workflows/**"
- "bun.lock"
- "package.json"
- "turbo.json"
- "apps/cloud/**"
- "packages/**"

selfhost_docker_smoke:
- ".github/workflows/**"
- ".dockerignore"
Expand Down Expand Up @@ -144,15 +165,18 @@ jobs:

test:
name: Test
runs-on: blacksmith-4vcpu-ubuntu-2404
runs-on: blacksmith-16vcpu-ubuntu-2404
timeout-minutes: 15
# Tuned for Blacksmith's 4 vCPU runners.
# Run eight independent packages at once and cap each Vitest process at two
# workers. This uses all 16 vCPUs without every nested runner seeing the
# whole machine and oversubscribing it.
env:
TURBO_API: ${{ vars.TURBO_API }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_REMOTE_CACHE_SIGNATURE_KEY: ${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }}
TURBO_TEST_CONCURRENCY: 4
TURBO_TEST_CONCURRENCY: 8
VITEST_MAX_WORKERS: 2
steps:
- uses: actions/checkout@v4

Expand Down Expand Up @@ -184,6 +208,8 @@ jobs:
# no signal. This is just a few file reads plus greps, so ~zero cost.
- run: bun run check:patches

- run: bun run --cwd e2e test:ci-shard

- run: bun run test

e2e:
Expand All @@ -192,14 +218,41 @@ jobs:
fail-fast: false
matrix:
include:
# Each cloud shard boots its own fresh dev stack. On 4 vCPU runners,
# four fatter shards keep the longest shard below selfhost while saving
# four runner boots and four warm cache restores.
- { target: cloud, shard: 1/4, shard-name: 1of4 }
- { target: cloud, shard: 2/4, shard-name: 2of4 }
- { target: cloud, shard: 3/4, shard-name: 3of4 }
- { target: cloud, shard: 4/4, shard-name: 4of4 }
- target: selfhost
# The planner assigns every file exactly once using recorded slow-file
# durations plus a conservative weight for new tests. The cloud DB's
# connection teardown and concurrent socket protocol have dedicated
# regression tests; these shards balance wall clock, not hide retries.
- { target: cloud, shard-index: 1, shard-name: 1of16 }
- { target: cloud, shard-index: 2, shard-name: 2of16 }
- { target: cloud, shard-index: 3, shard-name: 3of16 }
- { target: cloud, shard-index: 4, shard-name: 4of16 }
- { target: cloud, shard-index: 5, shard-name: 5of16 }
- { target: cloud, shard-index: 6, shard-name: 6of16 }
- { target: cloud, shard-index: 7, shard-name: 7of16 }
- { target: cloud, shard-index: 8, shard-name: 8of16 }
- { target: cloud, shard-index: 9, shard-name: 9of16 }
- { target: cloud, shard-index: 10, shard-name: 10of16 }
- { target: cloud, shard-index: 11, shard-name: 11of16 }
- { target: cloud, shard-index: 12, shard-name: 12of16 }
- { target: cloud, shard-index: 13, shard-name: 13of16 }
- { target: cloud, shard-index: 14, shard-name: 14of16 }
- { target: cloud, shard-index: 15, shard-name: 15of16 }
- { target: cloud, shard-index: 16, shard-name: 16of16 }
- { target: selfhost, shard-index: 1, shard-name: 1of10 }
- { target: selfhost, shard-index: 2, shard-name: 2of10 }
- { target: selfhost, shard-index: 3, shard-name: 3of10 }
- { target: selfhost, shard-index: 4, shard-name: 4of10 }
- { target: selfhost, shard-index: 5, shard-name: 5of10 }
- { target: selfhost, shard-index: 6, shard-name: 6of10 }
- { target: selfhost, shard-index: 7, shard-name: 7of10 }
- { target: selfhost, shard-index: 8, shard-name: 8of10 }
- { target: selfhost, shard-index: 9, shard-name: 9of10 }
- { target: selfhost, shard-index: 10, shard-name: 10of10 }
# Local files own their server, browser and data directory. Separate
# runners preserve that isolation while removing its 69-second serial
# lane from the two-minute critical path.
- { target: local, shard-index: 1, shard-name: 1of2 }
- { target: local, shard-index: 2, shard-name: 2of2 }
runs-on: blacksmith-4vcpu-ubuntu-2404
timeout-minutes: 30
steps:
Expand Down Expand Up @@ -235,26 +288,31 @@ jobs:

# Install from e2e so bunx resolves ITS pinned playwright (the version
# the tests run against) rather than floating to the latest.
# Blacksmith's Ubuntu image carries the official GitHub runner system
# dependencies. Restore the pinned browser binaries without apt-updating
# every matrix machine.
- name: Install Playwright Chromium
run: bunx playwright install --with-deps chromium chromium-headless-shell
run: bunx playwright install chromium chromium-headless-shell
working-directory: e2e

# The globalsetup boots the target's own dev server (ports are claimed
# per checkout, so this is hermetic) and tears it down after the run.
# --retry=2: browser scenarios can still hit isolated waitFor timeouts
# (single-test waitFor timeouts, not systemic failures); a retry on the
# same booted stack clears them.
- name: Run cloud scenarios
# Each target either boots its own shared dev server or lets each file own
# its server. Ports and data paths are hermetic in both cases.
# Do not retry scenarios: retries hide flakes and multiply slow timeout
# failures. The fixtures and process lifecycle are deterministic enough
# that the first result is the result.
- name: Run cloud shard
if: matrix.target == 'cloud'
env:
MCP_SESSION_TIMEOUT_MS: "3000"
MCP_PAUSED_SESSION_IDLE_TIMEOUT_MS: "6000"
run: bunx vitest run --project cloud --retry=2 ${{ matrix.shard && format('--shard={0}', matrix.shard) || '' }}
# Still 18x shorter than production, but long enough for a cold Vite
# resume route to compile under a fully loaded CI runner.
MCP_PAUSED_SESSION_IDLE_TIMEOUT_MS: "30000"
run: bun scripts/run-ci-shard.ts cloud ${{ matrix['shard-index'] }}
working-directory: e2e

- name: Run selfhost scenarios
if: matrix.target == 'selfhost'
run: bunx vitest run --project selfhost --retry=2
- name: Run scenarios
if: matrix.target != 'cloud'
run: bun scripts/run-ci-shard.ts ${{ matrix.target }} ${{ matrix['shard-index'] }}
working-directory: e2e

# Failed runs keep their trace.zip / session.mp4 / step screenshots in
Expand All @@ -267,13 +325,23 @@ jobs:
path: e2e/runs/
retention-days: 7

e2e-local:
name: E2E (stdio MCP)
# Skipped on pull_request: the local scenario boots a real `executor web`
# plus a browser and is currently flaky on PRs. Still runs on push to main.
if: github.event_name != 'pull_request'
cloud-closure:
name: Cloud evaluated closure
needs: changes
if: needs.changes.outputs.cloud_closure == 'true'
runs-on: blacksmith-4vcpu-ubuntu-2404
timeout-minutes: 20
env:
TURBO_API: ${{ vars.TURBO_API }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_REMOTE_CACHE_SIGNATURE_KEY: ${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }}
# Every cold isolate evaluates this closure before it can answer a
# request, and it only ever grows by accident - a barrel export or a new
# module-scope import quietly pulls megabytes into the server graph. The
# budget is a ratchet just above today's size, not a discovered limit:
# when it trips, make the new dependency lazy rather than raising it.
START_CLOSURE_BUDGET_MB: 13.5
steps:
- uses: actions/checkout@v4

Expand All @@ -289,41 +357,17 @@ jobs:
restore-keys: |
${{ runner.os }}-bun-1.3.11-

# The local scenarios boot a real `executor web` (which spawns a Node
# sidecar) and some drive a browser, so pin Node 24 and install Chromium.
- uses: actions/setup-node@v4
with:
node-version: 24

- run: bun install --frozen-lockfile

- name: Cache Playwright browsers
uses: actions/cache@v4
with:
path: ~/.cache/ms-playwright
key: ${{ runner.os }}-playwright-1.60.0
restore-keys: |
${{ runner.os }}-playwright-

# `chromium` and the new `chromium-headless-shell` ship as separate
# downloads; the browser-driven scenarios launch the headless shell.
# Install from e2e so bunx resolves ITS pinned playwright (the version the
# tests run against) rather than floating to the latest, which would fetch
# a browser build the test runtime does not look for.
- name: Install Playwright Chromium
run: bunx playwright install --with-deps chromium chromium-headless-shell
working-directory: e2e
- run: bun run build
working-directory: apps/cloud

# The `local` project is excluded from the default `test` chain (each
# scenario boots its own `executor web`). Run just the stdio MCP scenario
# here: it is the auto-connect / env-as-secret regression guard, and
# running it alone avoids the boot-resource accumulation and the
# pre-existing browser flakiness of the rest of the local suite. Expanding
# to the full `local` project (bun run test:local) is a follow-up once
# those are stabilized.
- name: Run the stdio MCP scenario
run: bunx vitest run --project local local/stdio-mcp.test.ts
working-directory: e2e
- run: node scripts/start-closure.mjs
working-directory: apps/cloud

desktop-smoke:
name: Desktop smoke build
Expand Down Expand Up @@ -375,7 +419,13 @@ jobs:
- name: Run sidecar outside the workspace
working-directory: apps/desktop
run: |
docker run --rm -d --name sidecar-smoke -p 45841:45841 -e HOME=/tmp \
# Containers do not inherit the job environment. Keep the compiled
# product smoke from becoming a production analytics/integrations hit.
docker run --rm -d --name sidecar-smoke -p 45841:45841 \
-e HOME=/tmp \
-e DO_NOT_TRACK=1 \
-e EXECUTOR_DISABLE_ANALYTICS=1 \
-e EXECUTOR_DISABLE_INTEGRATIONS_FETCH=1 \
-v "$PWD/resources/executor:/opt/executor:ro" \
debian:bookworm-slim /opt/executor/executor daemon run --foreground \
--port 45841 --hostname 0.0.0.0 --auth-token=ci-smoke
Expand Down
Loading
Loading