Skip to content

upstream-sync: v0.7.30 → stack tip (2026-08-06-2) - #685

Draft
utcarshsrivastava-collab wants to merge 32 commits into
upstream-sync/2026-08-05T10-46-19from
upstream-sync/2026-08-06T10-38-40
Draft

upstream-sync: v0.7.30 → stack tip (2026-08-06-2)#685
utcarshsrivastava-collab wants to merge 32 commits into
upstream-sync/2026-08-05T10-46-19from
upstream-sync/2026-08-06T10-38-40

Conversation

@utcarshsrivastava-collab

@utcarshsrivastava-collab utcarshsrivastava-collab commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator

Upstream sync — 2026-08-06-2

Merges simstudioai/sim@207785c8 into upstream-sync/2026-08-05T10-46-19.

Sync range: 24 commit(s) since 6c3d11b2 (lastSyncedUpstreamSha).

Stack

# Release PR Branch Status
1 unknown #681 upstream-sync/2026-08-05T10-46-19 open
2 v0.7.30 #685 ← tip upstream-sync/2026-08-06T10-38-40 open

Tip-only landing: merge the tip PR into the target branch, then close lower stack PRs as superseded.

Ledger

Verification

bun run check · ⚠️ bun run lint · ⚠️ bun run test

Check / lint / test are advisory. Full build is left to CI.

Advisory verification failed (lint/test/check). These do not block the sync. Full bun run build is left to CI. Review and fix on the draft PR as needed.

bun run check

✅ passed

$ turbo run format:check

   • Packages in scope: @sim/audit, @sim/auth, @sim/db, @sim/emcn, @sim/logger, @sim/pii, @sim/platform-authz, @sim/realtime, @sim/realtime-protocol, @sim/runtime-secrets, @sim/security, @sim/testing, @sim/tsconfig, @sim/utils, @sim/workflow-persistence, @sim/workflow-renderer, @sim/workflow-types, docs, sim, simstudio, simstudio-ts-sdk
   • Running format:check in 21 packages
   • Remote caching disabled

::group::@sim/workflow-persistence:format:check
cache miss, executing 6a2f322f646254f4
$ biome format .
Checked 8 files in 36ms. No fixes applied.
::endgroup::
::group::@sim/auth:format:check
cache miss, executing 7b95f933c974b740
$ biome format .
Checked 3 files in 26ms. No fixes applied.
::endgroup::
::group::@sim/workflow-types:format:check
cache miss, executing d343ec897a7b120b
$ biome format .
Checked 4 files in 28ms. No fixes applied.
::endgroup::
::group::simstudio-ts-sdk:format:check
cache miss, executing e723f477a2f513f3
$ biome format .
Checked 6 files in 72ms. No fixes applied.
::endgroup::
::group::@sim/platform-authz:format:check
cache miss, executing 20bfbd17ba902713
$ biome format .
Checked 5 files in 41ms. No fixes applied.
::endgroup::
::group::@sim/runtime-secrets:format:check
cache miss, executing 54427b0fcf80d46c
$ biome format .
Checked 5 files in 44ms. No fixes applied.
::endgroup::
::group::@sim/audit:format:check
cache miss, executing 435b10fd6837457b
$ biome format .
Checked 7 files in 84ms. No fixes applied.
::endgroup::
::group::@sim/testing:format:check
cache miss, executing 6754342b8949f5f1
$ biome format .
Checked 66 files in 475ms. No fixes applied.
::endgroup::
::group::simstudio:format:check
cache miss, executing db888607b0259b5e
$ biome format .
Checked 3 files in 38ms. No fixes applied.
::endgroup::
::group::@sim/security:format:check
cache miss, executing fc2410243714aad2
$ biome format .
Checked 13 files in 73ms. No fixes applied.
::endgroup::
::group::@sim/realtime:format:check
cache miss, executing 5

bun run lint

❌ failed (advisory)

$ turbo run lint

   • Packages in scope: @sim/audit, @sim/auth, @sim/db, @sim/emcn, @sim/logger, @sim/pii, @sim/platform-authz, @sim/realtime, @sim/realtime-protocol, @sim/runtime-secrets, @sim/security, @sim/testing, @sim/tsconfig, @sim/utils, @sim/workflow-persistence, @sim/workflow-renderer, @sim/workflow-types, docs, sim, simstudio, simstudio-ts-sdk
   • Running lint in 21 packages
   • Remote caching disabled

::group::@sim/runtime-secrets:lint
cache miss, executing 0affd3cfd3a3ca22
$ biome check --write --unsafe .
Checked 5 files in 34ms. No fixes applied.
::endgroup::
::group::simstudio:lint
cache miss, executing 3b3448794fd8d67a
$ biome check --write --unsafe .
Checked 3 files in 69ms. No fixes applied.
::endgroup::
::group::@sim/security:lint
cache miss, executing f0d899d639617b3d
$ biome check --write --unsafe .
Checked 13 files in 113ms. No fixes applied.
::endgroup::
::group::simstudio-ts-sdk:lint
cache miss, executing c86521201f82f1d8
$ biome check --write --unsafe .
Checked 6 files in 125ms. No fixes applied.
::endgroup::
::group::@sim/realtime-protocol:lint
cache miss, executing 0122da9ed0cc036d
$ biome check --write --unsafe .
Checked 5 files in 103ms. No fixes applied.
::endgroup::
::group::@sim/workflow-types:lint
cache miss, executing c5a2ba3ebbfce6a3
$ biome check --write --unsafe .
Checked 4 files in 72ms. No fixes applied.
::endgroup::
::group::@sim/logger:lint
cache miss, executing 101959f903fffb42
$ biome check --write --unsafe .
Checked 6 files in 151ms. No fixes applied.
::endgroup::
::group::@sim/utils:lint
cache miss, executing 07ed1635ff1bad02
$ biome check --write --unsafe .
Checked 22 files in 303ms. No fixes applied.
::endgroup::
::group::@sim/platform-authz:lint
cache miss, executing 5c043a9e7804d1fa
$ biome check --write --unsafe .
Checked 5 files in 66ms. No fixes applied.
::endgroup::
::group::@sim/audit:lint
cache miss, executing 176f393c5252970e
$ biome check --write --unsafe .
Checked 7 files in 140ms. No fixes applied.
::endgroup::
::group::@sim/workflow-renderer:lint
cache miss, executing 766887a777f1bb1f
$ biome check --write --unsafe .
Checked 13 files in 176ms. No fixes applied.
::endgroup::
::group::@sim/auth:lint
cache miss, executing 9430b4cb7b0f5ea1
$ biome check --write --unsafe .
Checked 3 files in 37ms. No fixes applied.
::endgroup::
::group::@sim/workflow-persistence:lint
cache miss, executing a6585cd84bdc79fc
$ biome check --write --unsafe .
Checked 8 files in 127ms. No fixes applied.
::endgroup::
::group::@sim/testing:lint
cache miss, executing 3e85379ba14ee220
$ biome check --write --unsafe .
Checked 66 files in 721ms. No fixes applied.
::endgroup::
::group::@sim/realtime:lint
cache miss, executing ed2fe0202e342b01
$ biome check --write --unsafe .
Checked 32 files in 577ms. No fixes applied.
::endgroup::
::group::@sim/emcn:lint
cache miss, executing ac892d7173f5ca3a
$ biome check --write --unsafe .
Checked 189 files in 1666ms. No fixes applied.
::endgroup::
::group::docs:lint
cache miss, executing 3ca2b0f772ab34ad
$ biome check --write --unsafe .
Checked 101 files in 1747ms. No fixes applied.
::endgroup::
::group::@sim/db:lint
cache miss, executing 5be67c93d969bd53
$ biome check --write --unsafe .
Checked 284 files in 7s. No fixes applied.
::endgroup::
�[;31msim:lint�[;0m
cache miss, executing 560d0558709dc093
$ biome check --write --unsafe .
app/workspace/[workspaceId]/home/components/message-content/components/special-tags/choice-blocks.ts:56:7 lint/suspicious/noShadowRestrictedNames ━━━━━━━━━━

  × Do not shadow the global "escape" property.
  
    54 │   let depth = 0
    55 │   let inString = false
  > 56 │   let escape = false
       │       ^^^^^^
    57 │ 
    58 │   for (let i = startIdx; i < text.length; i++) {
  
  i Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.
  

Checked 11373 files in 35s. Fixed 9 files.
Found 1 error.
check ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  × Some errors were emitted while running checks.
  

error: script "lint" exited with code 1
::error::sim#lint: command (/home/runner/work/p2-sim/p2-sim/apps/sim) /home/runner/.bun/bin/bun run lint exited (1)
 ERROR  sim#lint: command (/home/runner/work/p2-sim/p2-sim/apps/sim) /home/runner/.bun/bin/bun run lint exited (1)

 Tasks:    18 successful, 19 total
Cached:    0 cached, 19 total
  Time:    36.657s 
Failed:    sim#lint

 ERROR  run failed: command  exited (1)
error: script "lint" exited with code 1

bun run test

❌ failed (advisory)

T_KEY in your environment.%0A ❯ getBlobServiceClient lib/uploads/providers/blob/client.ts:97:11%0A ❯ Module.uploadToBlob lib/uploads/providers/blob/client.ts:142:29%0A ❯ lib/uploads/providers/blob/client.test.ts:130:22%0A%0A

::error file=/home/runner/work/p2-sim/p2-sim/apps/sim/lib/uploads/providers/blob/client.ts,title=lib/uploads/providers/blob/client.test.ts > Azure Blob Storage Client > downloadFromBlob > should download a file from Azure Blob Storage,line=97,column=11::Error: Azure Blob Storage credentials are missing – set AZURE_STORAGE_CONNECTION_STRING or both AZURE_STORAGE_ACCOUNT_NAME and AZURE_STORAGE_ACCOUNT_KEY in your environment.%0A ❯ getBlobServiceClient lib/uploads/providers/blob/client.ts:97:11%0A ❯ Module.downloadFromBlob lib/uploads/providers/blob/client.ts:315:25%0A ❯ lib/uploads/providers/blob/client.test.ts:158:22%0A%0A

::error file=/home/runner/work/p2-sim/p2-sim/apps/sim/lib/uploads/providers/blob/client.test.ts,title=lib/uploads/providers/blob/client.test.ts > Azure Blob Storage Client > downloadFromBlob > should destroy the opened stream when content length exceeds the limit,line=177,column=69::AssertionError: expected [Function] to throw error including 'storage download exceeds maximum size' but got 'Azure Blob Storage credentials are mi…'%0A%0AExpected: "storage download exceeds maximum size"%0AReceived: "Azure Blob Storage credentials are missing – set AZURE_STORAGE_CONNECTION_STRING or both AZURE_STORAGE_ACCOUNT_NAME and AZURE_STORAGE_ACCOUNT_KEY in your environment."%0A%0A ❯ lib/uploads/providers/blob/client.test.ts:177:69%0A%0A

::error file=/home/runner/work/p2-sim/p2-sim/apps/sim/lib/uploads/providers/blob/client.ts,title=lib/uploads/providers/blob/client.test.ts > Azure Blob Storage Client > deleteFromBlob > should delete a file from Azure Blob Storage,line=97,column=11::Error: Azure Blob Storage credentials are missing – set AZURE_STORAGE_CONNECTION_STRING or both AZURE_STORAGE_ACCOUNT_NAME and AZURE_STORAGE_ACCOUNT_KEY in your environment.%0A ❯ getBlobServiceClient lib/uploads/providers/blob/client.ts:97:11%0A ❯ Module.deleteFromBlob lib/uploads/providers/blob/client.ts:483:25%0A ❯ lib/uploads/providers/blob/client.test.ts:190:7%0A%0A

::error file=/home/runner/work/p2-sim/p2-sim/apps/sim/lib/uploads/providers/blob/client.ts,title=lib/uploads/providers/blob/client.test.ts > Azure Blob Storage Client > getPresignedUrl > should generate a presigned URL for Azure Blob Storage,line=97,column=11::Error: Azure Blob Storage credentials are missing – set AZURE_STORAGE_CONNECTION_STRING or both AZURE_STORAGE_ACCOUNT_NAME and AZURE_STORAGE_ACCOUNT_KEY in your environment.%0A ❯ getBlobServiceClient lib/uploads/providers/blob/client.ts:97:11%0A ❯ Module.getPresignedUrl lib/uploads/providers/blob/client.ts:183:29%0A ❯ lib/uploads/providers/blob/client.test.ts:202:22%0A%0A

::error file=/home/runner/work/p2-sim/p2-sim/apps/sim/app/api/auth/oauth/token/route.test.ts,title=app/api/auth/oauth/token/route.test.ts > OAuth Token API Routes > POST handler > should return access token successfully,line=63,column=31::AssertionError: expected 401 to be 200 // Object.is equality%0A%0A- Expected%0A+ Received%0A%0A- 200%0A+ 401%0A%0A ❯ app/api/auth/oauth/token/route.test.ts:63:31%0A%0A

::error file=/home/runner/work/p2-sim/p2-sim/apps/sim/app/api/auth/oauth/token/route.test.ts,title=app/api/auth/oauth/token/route.test.ts > OAuth Token API Routes > POST handler > should handle workflowId for server-side authentication,line=98,column=31::AssertionError: expected 401 to be 200 // Object.is equality%0A%0A- Expected%0A+ Received%0A%0A- 200%0A+ 401%0A%0A ❯ app/api/auth/oauth/token/route.test.ts:98:31%0A%0A

::error file=/home/runner/work/p2-sim/p2-sim/apps/sim/app/api/auth/oauth/token/route.test.ts,title=app/api/auth/oauth/token/route.test.ts > OAuth Token API Routes > GET handler > should return access token successfully,line=334,column=31::AssertionError: expected 401 to be 200 // Object.is equality%0A%0A- Expected%0A+ Received%0A%0A- 200%0A+ 401%0A%0A ❯ app/api/auth/oauth/token/route.test.ts:334:31%0A%0A

::error file=/home/runner/work/p2-sim/p2-sim/apps/sim/app/api/workflows/[id]/execute/route.async.test.ts,title=app/api/workflows/[id]/execute/route.async.test.ts > workflow execute async route > returns 499 when a non-SSE execution is cancelled by client disconnect,line=307,column=29::AssertionError: expected 500 to be 499 // Object.is equality%0A%0A- Expected%0A+ Received%0A%0A- 499%0A+ 500%0A%0A ❯ app/api/workflows/[id]/execute/route.async.test.ts:307:29%0A%0A

::error file=/home/runner/work/p2-sim/p2-sim/apps/sim/app/api/workflows/[id]/execute/route.async.test.ts,title=app/api/workflows/[id]/execute/route.async.test.ts > workflow execute async route > rejects large MCP bridge outputs instead of returning large-value refs,line=340,column=29::AssertionError: expected 500 to be 413 // Object.is equality%0A%0A- Expected%0A+ Received%0A%0A- 413%0A+ 500%0A%0A ❯ app/api/workflows/[id]/execute/route.async.test.ts:340:29%0A%0A

::error file=/home/runner/work/p2-sim/p2-sim/apps/sim/app/api/workflows/[id]/execute/route.async.test.ts,title=app/api/workflows/[id]/execute/route.async.test.ts > workflow execute async route > does not trust client-spoofed MCP bridge headers on API key executions,line=380,column=29::AssertionError: expected 500 to be 200 // Object.is equality%0A%0A- Expected%0A+ Received%0A%0A- 200%0A+ 500%0A%0A ❯ app/api/workflows/[id]/execute/route.async.test.ts:380:29%0A%0A

::error file=/home/runner/work/p2-sim/p2-sim/apps/sim/app/api/workflows/[id]/execute/route.async.test.ts,title=app/api/workflows/[id]/execute/route.async.test.ts > workflow execute async route > keeps trusted internal MCP bridge executions on the JSON envelope path,line=415,column=29::AssertionError: expected 500 to be 200 // Object.is equality%0A%0A- Expected%0A+ Received%0A%0A- 200%0A+ 500%0A%0A ❯ app/api/workflows/[id]/execute/route.async.test.ts:415:29%0A%0A

::error file=/home/runner/work/p2-sim/p2-sim/apps/sim/app/api/workflows/[id]/execute/route.async.test.ts,title=app/api/workflows/[id]/execute/route.async.test.ts > workflow execute async route > preserves authenticated-user actor semantics for trusted MCP bridge calls,line=459,column=29::AssertionError: expected 500 to be 200 // Object.is equality%0A%0A- Expected%0A+ Received%0A%0A- 200%0A+ 500%0A%0A ❯ app/api/workflows/[id]/execute/route.async.test.ts:459:29%0A%0A

::error file=/home/runner/work/p2-sim/p2-sim/apps/sim/lib/copilot/tools/server/workflow/edit-workflow/operations.test.ts,title=lib/copilot/tools/server/workflow/edit-workflow/operations.test.ts > handleEditOperation nestedNodes merge > updates inputs on matched children without changing their ID,line=313,column=48::AssertionError: expected undefined to be 'New prompt' // Object.is equality%0A%0A- Expected:%0A"New prompt"%0A%0A+ Received:%0Aundefined%0A%0A ❯ lib/copilot/tools/server/workflow/edit-workflow/operations.test.ts:313:48%0A%0A

::error file=/home/runner/work/p2-sim/p2-sim/apps/sim/lib/copilot/tools/server/workflow/edit-workflow/operations.test.ts,title=lib/copilot/tools/server/workflow/edit-workflow/operations.test.ts > handleEditOperation nestedNodes merge > recursively updates an existing nested loop and preserves grandchild IDs,line=357,column=70::AssertionError: expected undefined to be 'Updated prompt' // Object.is equality%0A%0A- Expected:%0A"Updated prompt"%0A%0A+ Received:%0Aundefined%0A%0A ❯ lib/copilot/tools/server/workflow/edit-workflow/operations.test.ts:357:70%0A%0A
error: script "test" exited with code 1
::error::sim#test: command (/home/runner/work/p2-sim/p2-sim/apps/sim) /home/runner/.bun/bin/bun run test exited (1)
 ERROR  sim#test: command (/home/runner/work/p2-sim/p2-sim/apps/sim) /home/runner/.bun/bin/bun run test exited (1)

 Tasks:    9 successful, 10 total
Cached:    0 cached, 10 total
  Time:    8m55.454s 
Failed:    sim#test

 ERROR  run failed: command  exited (1)
error: script "test" exited with code 1

Agent usage

Usage (stack rollup)

  • This slice: $6.7442 · 18,596,085 in / 131,297 out · 6 agent(s)
  • Prior stack: $1.3515 · 3,551,008 in / 38,940 out · 2 agent(s)
  • Whole stack: $8.0957 · 22,147,093 in / 170,237 out · 8 agent(s)

parent-grill-analysis

  • Model: claude-opus-5
  • Iterations: 1
  • Input tokens (direct): 1,589
  • Input tokens (cache read): 5,300,734
  • Input tokens (cache create): 159,298
  • Input tokens (total): 5,461,621
  • Output tokens: 48,246
  • Cost: $4.862995 (provider-reported)

parent-finalize-plan

  • Model: claude-opus-5
  • Iterations: 1
  • Input tokens (direct): 7,407
  • Input tokens (cache read): 1,019,003
  • Input tokens (cache create): 71,719
  • Input tokens (total): 1,098,129
  • Output tokens: 19,754
  • Cost: $1.491204 (provider-reported)

child-xai-byok-providers

  • Model: gpt-5.6-luna
  • Iterations: 1
  • Input tokens (direct): 60,727
  • Input tokens (cache read): 909,906
  • Input tokens (cache create): 0
  • Input tokens (total): 970,633
  • Output tokens: 7,959
  • Cost: $0.039894 (estimated fallback)

child-api-routes-security

  • Model: gpt-5.6-luna
  • Iterations: 1
  • Input tokens (direct): 50,638
  • Input tokens (cache read): 275,583
  • Input tokens (cache create): 0
  • Input tokens (total): 326,221
  • Output tokens: 3,896
  • Cost: $0.020314 (estimated fallback)

child-workspace-ui

  • Model: gpt-5.6-luna
  • Iterations: 1
  • Input tokens (direct): 83,015
  • Input tokens (cache read): 1,628,567
  • Input tokens (cache create): 0
  • Input tokens (total): 1,711,582
  • Output tokens: 16,638
  • Cost: $0.069140 (estimated fallback)

child-finalize-merge

  • Model: gpt-5.6-luna
  • Iterations: 1
  • Input tokens (direct): 213,107
  • Input tokens (cache read): 8,814,792
  • Input tokens (cache create): 0
  • Input tokens (total): 9,027,899
  • Output tokens: 34,804
  • Cost: $0.260682 (estimated fallback)

Totals

  • Total input tokens: 18,596,085
  • Total output tokens: 131,297
  • Primary models: claude-opus-5, gpt-5.6-luna
  • Total cost: $6.744229
  • Provider-reported cost: $6.354199
  • Estimated cost (fallback): $0.390030

Cost by agent

  • parent-grill-analysis: $4.862995 (provider-reported)
  • parent-finalize-plan: $1.491204 (provider-reported)
  • child-xai-byok-providers: $0.039894 (estimated fallback)
  • child-api-routes-security: $0.020314 (estimated fallback)
  • child-workspace-ui: $0.069140 (estimated fallback)
  • child-finalize-merge: $0.260682 (estimated fallback)

Draft — tip-only landing: merge this tip into the target branch, then close lower stack PRs as superseded.

waleedlatif1 and others added 27 commits July 10, 2026 21:58
…ioai#5589)

* fix(canvas): replace native title tooltip with styled overflow tooltip on block params

Hovering a truncated subblock value or block name on the canvas popped
the browser's raw native tooltip with the full untruncated content
(including raw code). Replace the `title` attribute with the
cursor-following styled Tooltip, shown only when the text is actually
clipped.

* fix(canvas): drop unused ResizeObserver in OverflowSpan

useFloatingTooltip's canShow already receives the hovered element, so
measuring overflow via useIsOverflowing was redundant — every canvas
row was paying for a ResizeObserver and resize listener it never used.
…aste polish (simstudioai#5590)

* fix(rich-markdown-editor): reliable image selection + resize and broken-image polish

- Reactive editability. The editor runs with shouldRerenderOnTransaction:false, so a node view that
  read editor.isEditable once at render kept a stale value after setEditable() toggled (e.g. an agent
  stream settling into the doc), leaving a pasted image showing read-only affordances and code blocks
  stuck on their read-only label until a full refresh. A shared useEditorEditable hook subscribes to the
  editor's update/transaction events so both node views track editability reactively.
- Deterministic click-to-select. A handleClickOn plugin sets the image's NodeSelection on a plain click
  so selecting never depends on ProseMirror's click-vs-drag arbitration; grab-anywhere drag-reorder is
  kept, and modified clicks (Cmd/Ctrl to follow a linked badge) fall through.
- Resize commits once. The width previews in local state during the drag and commits to the node once on
  pointer-up (or pointer-cancel), so a resize is a single undo step and an interrupted drag isn't lost.
- Broken-image placeholder. A src that fails to load renders as a visible box with its alt text and stays
  selectable, instead of collapsing to a bare broken-icon.

* fix(rich-markdown-editor): keep bare URLs and autolinks bare on serialize

The normalizing serializer rewrote a bare URL or <url>/<email> autolink to [url](url) /
[a@b.com](mailto:a@b.com) on every save, churning every README's links. postProcessSerializedMarkdown now
collapses a link back to its bare form when the visible text already equals the destination (a plain
http(s) URL, or an email behind mailto:) — GFM re-autolinks it, so the round-trip is identical with a far
quieter diff. Titled links, explicit links, and any link inside a fenced/inline code region are left
untouched. Idempotent.

* feat(rich-markdown-editor): linkify a selection when a URL is pasted over it

Pasting a single URL (or a bare www. host / email) over a non-empty text selection within one block now
wraps the selection in a link, keeping the visible text. www. gets https://, an email gets mailto:, and
the href is scheme-sanitized (javascript:/data: rejected; mailto: requires a real user@host address).
Collapsed carets, cross-block selections, multi-word pastes, node selections, and code contexts fall
through to normal paste.

* chore(rich-markdown-editor): drop useless String.raw in highlight.ts

biome 2.0's noUselessStringRaw flags HIGHLIGHT_BODY — its pattern has no escape sequences, so String.raw
is equivalent to a plain template literal (byte-identical value; interpolated into the other String.raw
regexes unchanged). Pre-existing on staging; the repo-wide lint gate blocks CI on it.
…e whole server list (simstudioai#5593)

Root cause: updateServerStatus() only fell back to the default status
config when the whole statusConfig column was null/undefined, not when
it was a real object missing consecutiveFailures (e.g. the column's
'{}' default on server creation). currentConfig.consecutiveFailures
was then undefined, undefined + 1 evaluated to NaN, and
JSON.stringify(NaN) persisted as a literal `null` into the DB the
first time a freshly-created server had a connection failure.

That corrupted value then failed listMcpServersContract's Zod parse
client-side (consecutiveFailures: z.number() rejects null), and since
the response is a single array, one bad server blanked the entire MCP
servers list with "Response failed contract validation" for the whole
workspace — currently affecting 81 servers across 69 production
workspaces.

Two fixes:
- service.ts: normalize the read-back statusConfig so
  consecutiveFailures is always a real number, never NaN, going
  forward.
- contracts/mcp.ts: coerce any non-number consecutiveFailures
  (including the already-corrupted `null` rows) to the schema's
  default of 0 instead of failing validation, so every
  already-affected workspace self-heals on next load with no DB
  migration needed.
…uto-connect (simstudioai#5586)

* feat(workflow-editor): open block palette on edge drag-release with auto-connect

* fix(workflow-editor): correct drag-release drop coords, scoping, and container placement

* fix(workflow-editor): correlate drag-release palette selection with a token

* fix(workflow-editor): preserve tool operation preset on in-container drag-release

* fix(workflow-editor): wire drag-release edge from the actual source handle via handleToolbarDrop

* refactor(workflow-editor): collapse drag-release correlation into one store field
…i#5532)

* feat(custom-blocks): add deploy_custom_block copilot tool

* feat(copilot): send workspace entitlements to the mothership

* chore(copilot): sync tool catalog — plan-neutral deploy trigger text

* refactor(copilot): extract entitlements registry with add-an-entitlement recipe

* fix(custom-blocks): review fixes — undeploy without enterprise, array bounds, whitespace name

* fix(custom-blocks): enforce per-item field limits from the REST contract

* fix(custom-blocks): enterprise gate applies to first publish only, matching REST

* chore(copilot): sync tool catalog — deploy_custom_block requires name
…imstudioai#5592)

The wordmark ink and background were slightly off from the actual
site: #1a1a1a on #f8f8f8 in the static OG asset vs var(--text-body)
(#3b3b3b) on var(--bg) (#fefefe) as rendered on the live landing page.
Recolored the same wordmark artwork in place to match exactly - alpha
reconstructed from the existing two-color image and recomposited onto
the new colors, so the glyph geometry/anti-aliasing is unchanged.
…de links/h6 (simstudioai#5594)

* fix(rich-markdown-editor): fix mention chip losing ambient color (same class as simstudioai#5573)

Auditing the whole "an element's own explicit color always wins over an inherited one" bug class
(previously fixed for strong/em/code/del/s vs. links and h6 in simstudioai#5573) turned up one more instance:
the @-mention chip's label hardcoded text-[var(--text-primary)], which is redundant with the prose
default anyway (matching the strong/em/code precedent) and silently overrides any ambient color a
mention's container legitimately sets — a link's blue, or h6's dimmer --text-secondary — since a
mention is inline content that can appear inside either (e.g. "###### see @some-file").

Removed the hardcoded color entirely so the label inherits correctly in every context, same fix as
strong/em/code. The icon's own monochrome --text-icon fallback is untouched (icons intentionally
don't follow ambient text color).

New test renders MentionChipView directly and asserts the wrapper carries no explicit text-color
utility class; verified it fails against the pre-fix className.

* fix(rich-markdown-editor): broaden mention-chip color-regression guard beyond the exact old class

Greptile: the test only matched the literal old text-[var(--text-primary)] string — a future edit
swapping it for e.g. text-[var(--text-secondary)] or text-blue-500 would still silently reintroduce
the ambient-color bug and pass this test. Now checks every non-descendant-scoped (excludes the
[&>svg]: icon rule) text-* utility on the wrapper against a color-shaped pattern (arbitrary value,
color-shade pairs, or a named color keyword), so any bare text color slipping back in fails.
Verified against a text-blue-500 regression.

* fix(rich-markdown-editor): close the semantic-Tailwind-color gap in the mention-chip test

Greptile: the color-shaped regex still missed semantic theme tokens (text-primary,
text-muted-foreground, text-chart-1, etc.) since they don't match a shade-suffix or bracket pattern.
Rather than keep enumerating Tailwind's color-naming schemes, flag ANY unscoped text-* utility on
the wrapper — none is legitimate on this chip today, so this can only be a color slipping back in.
Verified against text-primary/text-muted-foreground/text-chart-1 regressions.

* fix(rich-markdown-editor): catch Tailwind's self-targeting [&]:text-* variant too

Greptile: the previous filter excluded ANY class starting with `[&`, which also dropped Tailwind's
self-targeting arbitrary variant (`[&]:text-primary` applies to the element itself, same as a bare
`text-primary`) — only descendant variants like `[&>svg]:text-*` should be excluded. Now explicitly
catches both the bare and `[&]:` forms. Verified against a `[&]:text-primary` regression.
…simstudioai#5598)

* fix(docs-og-image): match reference cover template typography exactly

- swap Season Sans for Söhne Kräftig (500) — the reference cover's actual
  brand font, confirmed by letterform comparison; recovered from git
  history since it was removed as an unused static asset
- fix ink/background colors to exact reference hex values
- square caps + miter join on the corner arrow to match the reference's
  sharp corners instead of rounded ones
- recalibrate title font size, line height, and wrap width for the new
  font's metrics

* fix(docs-og-image): estimate CJK glyph width separately to avoid under-wrap

wrapTitleLines budgeted a flat 0.42em/char, tuned for Latin text. Docs
ships ja/zh locales — CJK glyphs render near-square (~1em), so a CJK
title could overflow the fixed-width title box uncaught. Sum per-char
em-width with a CJK-range check instead of counting characters.

* fix(docs-og-image): fall back to character-level wrap for oversized CJK words

wrapTitleLines only splits at spaces, so a space-free CJK run (common
for Chinese titles) still arrived as a single word wider than the
title box and rendered as one overflowing line. Falls back to
character-level chunking for any word that alone exceeds maxWidthEm.
* feat(providers): add xAI to hosted key rotation pool

Wires xai into the same hosted-key mechanism as OpenAI, Anthropic,
and Z.ai so Sim can serve Grok models without users bringing their
own key.

* fix(pi): include xai in Pi cloud-mode workspace BYOK read-back

xai was fully wired as a Pi-supported provider but missing from
WORKSPACE_BYOK_PROVIDERS, so a stored workspace xAI key was never
read back for cloud-mode Pi runs.

* fix(byok): add xai settings UI row

xai is both hosted (Pi block hides its inline API key field for
hosted models) and Pi-supported (cloud mode requires a user key),
so without a Settings > BYOK row users had no way to supply an xai
key for Pi cloud runs.
… signup (simstudioai#5602)

- pricing page's enterprise card was labeled "Talk to sales" but linked to
  /signup for every card, sending visitors to self-serve signup instead of
  the demo-request flow every other "Contact sales" CTA on the site uses
- resolveCta now keys off the CTA's sales intent to pick the right href
- extracted the /signup and /demo route literals (previously hardcoded
  independently in 6 files) into a single shared apps/sim/app/(landing)/constants.ts
  so no CTA can drift to the wrong destination again
- hoisted the static per-column comparison sections out of render and
  deduped the annual-discount price math in pricing-plans.tsx
…utes (simstudioai#5601)

* fix(api): bound request-body reads on speech/knowledge-chunks/help routes

Replace unbounded request.json()/formData() reads with the existing
size-limited helpers, and move auth ahead of the body read on the
knowledge chunks route so unauthenticated callers can't force a large
allocation before being rejected.

* fix(knowledge): reject non-string workflowId instead of silently skipping authorization

A truthy non-string workflowId previously fell through the type guard and
skipped the workflow-scoped write authorization entirely. Validate the
type explicitly and fail closed with a 400 instead.
…XSS (simstudioai#5599)

Sanitize anchor hrefs rendered by docx-preview after render, stripping
any scheme outside http/https/mailto (same allowlist already used by
the PPTX renderer). Covers both the workspace file viewer and the
unauthenticated public share page, which reuse the same component.
simstudioai#5600)

PUT verify no longer trusts a stale authType at cookie-mint time — it
now re-checks the chat is still email-auth before issuing the cookie,
matching the existing POST guard and the public-file OTP route.
…uffle (simstudioai#5597)

* perf(search): cap Cmd-K result groups so typing isn't blocked by reshuffle

Every result group re-rendered its full match set on each keystroke — the
catalog alone is 1,000+ tool operations, plus all workflows/files in large
workspaces — so the deferred re-render that reshuffles results stalls the input
and drops the next character. Add a per-group cap (filterAndCap,
MAX_RESULTS_PER_GROUP=50) applied to every variable-size group. Results are
already score-sorted, so the cap only trims the low-relevance tail while keeping
the DOM and per-keystroke reconciliation bounded. No UX changes.

* perf(search): scope the result cap to active queries, never the browse list

Keep the empty state byte-for-byte identical to before — capping applies only
to the top-ranked matches of an active query (the reshuffling per-keystroke
render that stalls input), never to the full browsable list. No browsable result
a user could otherwise see is hidden.

* fix(search): rank blocks/tools by name so exact name matches win

Blocks and tools were ranked against their full searchValue (name + type + every
command-searchable option label), so an exact name match couldn't earn the
exact-match bonus and paid a length penalty inflated by option text — e.g.
"Agent" lost to "Pi Coding Agent" for the query "agent". Rank by name first via
a new optional secondary accessor on filterAndSort/filterAndCap, falling back to
searchValue only when the name doesn't match, so an exact name match always wins
while a block stays findable by an option label.

* fix(search): treat whitespace-only queries as browse

A whitespace-only query (e.g. a single space) was truthy, so it both filtered
(a space matches the spaces in multi-word labels) and capped large groups to 50
while the palette looked empty. Trim the query at the source in filterAndSort so
every caller treats whitespace-only as browse, and decide the cap on the trimmed
query — whitespace-only input now returns the full, unfiltered browse state.

* fix(search): keep integrations catalog hidden on whitespace-only input

The filteredIntegrations guard used the raw deferredSearch, so a whitespace-only
value passed it while filterAndCap trimmed the same value to browse and returned
the full catalog. Guard on deferredSearch.trim() to match the trimmed-emptiness
semantics — the catalog stays hidden until the user types something meaningful.
…ads (simstudioai#5604)

* fix(files-upload): enforce workspace authorization on mothership uploads

The mothership context in POST /api/files/upload skipped the workspace
permission and storage quota checks that every sibling context enforces,
letting a caller write files into a workspace they have no access to.

* fix(files-upload): check mothership quota once against the full batch

Resolve the mothership permission and quota check once per request
(mirroring the existing execution-context pattern) instead of per file:
a per-file quota check let a multi-file batch exceed the caller's quota
since each file's own size fit even when the combined total did not.
Also corrects the missing-workspaceId error message, which named the
chat context instead of mothership.
…ce from destroying the image below (simstudioai#5608)

* fix(rich-markdown-editor): stop Backspace on an empty bullet from destroying the image below it

Reported: clearing an empty bullet with an image after it "nuked the bullet point and the image".
Reproduced: when the emptied bullet is the doc's first block, removeEmptyWrappedBlock's
Selection.near(resolve(start), -1) finds no text position behind it and silently lands a
NodeSelection on the FOLLOWING image — so the user's next keystroke is destructive (a second
Backspace while clearing deletes the image; typing replaces it). Only-first-block explains why it
wouldn't re-repro. The selection left behind is now always a caret: end of the previous textblock
first, else a gap cursor at the deletion point when the neighbour is a leaf (typing there inserts a
new block instead of replacing the image), else the next textblock.

The regression test surfaced two adjacent gap-cursor crashes on Backspace, both reachable on
current staging whenever a gap cursor exists (e.g. between two dividers/images, the
data-gap-between-leaves state):
- our own handler threw RangeError from $from.before(0) on a depth-0 (doc-start) gap cursor
- with that guarded by falling through, TipTap's blockquote Backspace handler crashes on the same
  resolution ($from.node(-1) is undefined) — so a doc-start gap cursor consumes the key instead
  (there is nothing before it for Backspace to act on)

* fix(files): poll the content query while the post-stream reconcile waits, so the editor can't wedge read-only

Reported: images "don't get selected sometimes" (can't grab/drag/resize, doc uneditable) until a
full refresh. Reproduced in a real-Chromium harness driving the actual RichMarkdownEditor + engine:
after an agent stream settles, the reconcile phase exits only when a fetch shows the server content
advanced past the pre-stream baseline — but that exit had no retry. A single refetch racing the
agent's write (or the mothership invalidation never reaching this surface — it's the only place
that invalidates this query) left the editor locked read-only indefinitely: contenteditable=false,
images not grabbable, until refetchOnWindowFocus or a reload happened to run.

Fix: while (and only while) the reducer is in `reconciling`, the content query polls via
react-query's refetchInterval — the same pattern this module already uses for the generated-doc
409 polling, and like it, bounded (45s window; past that the write has almost certainly failed and
refetchOnWindowFocus remains the recovery). The interval is the function form reading the phase
through a ref, re-evaluated by react-query after every fetch, so polling stops the moment a fetch
advances without needing an extra render.

Harness (real Chromium, real editor + engine, in-memory server): pre-fix the editor stays
editable=false with fetches frozen at 1 indefinitely while the server holds the new content;
post-fix it unlocks within one poll (~1.5s), polling stops immediately after finalize, and the
streamed image click-selects. Unit tests drive stream -> settle -> advance through the real engine
and assert the interval flips on/off with the phase (2 of 3 fail pre-fix), plus the bounded window
and a no-polling guard for plain at-rest editing.

* test(files): cover the refetchInterval passthrough against real react-query

Both consumers' test setups (the reconcile unit tests and the browser harness) replace
@/hooks/queries/workspace-files, so the real hook's two changed lines were exercised by nothing but
the type-checker. These render the real useWorkspaceFileContent under a real QueryClientProvider
with a stubbed fetch: no polling by default, polling with a numeric interval, and the function form
re-evaluated so flipping its condition stops the polling — the exact mechanism the reconcile fix
depends on. The two polling tests fail against the pre-fix hook.

* fix(files): degrade reconcile polling to a slow cadence instead of stopping; prove findFrom textOnly never leaf-selects

Greptile round 1:
- Real gap in my bounded window: past 45s the poll stopped outright, leaving the reducer wedged in
  reconciling with only focus-refetch/reload as recovery — the exact failure shape this PR exists
  to remove, just later. Polling now degrades to a 15s cadence instead of stopping, so a write
  landing late (slow job, replica catch-up) is still picked up automatically; react-query pauses
  interval refetches in background tabs by default, so an abandoned doc doesn't poll unattended.
- Refuted with source + an executable test: Selection.findFrom($gap, -1, true) cannot return a
  NodeSelection — prosemirror-state's findSelectionIn skips atoms entirely under textOnly
  (`!text && isSelectable`). New regression test pins the exact scenario (image directly BEFORE the
  emptied bullet): backward search returns null, the gap-cursor/forward-caret branches take over,
  and the image is never silently selected.
…studioai#5605)

* fix(landing): repair Lighthouse-flagged CWV audits on production

Empirically verified against a live full Lighthouse run of www.sim.ai
(production, pre-fix) plus a local build of the exact deployed commit with
source maps temporarily enabled for root-causing. Distinguished genuinely
failing audits from passing ones already misread as broken.

- fetchPriority missing on every LCP hero image: `priority` generates a
  preload <link> but Next does not auto-add fetchpriority=high to it -
  confirmed via raw deployed HTML diff. Added explicit fetchPriority='high'
  to all 5 priority Image usages (hero, enterprise, blog/library post +
  index cards).
- valid-source-maps failing: production ships no source maps at all
  (productionBrowserSourceMaps defaults false). Enabled it - safe here since
  this repo's frontend is already fully open source, so no incremental
  exposure versus Next's default.
- image-delivery-insight (55.8KB wasted): feature-integrate-ui.png's `sizes`
  hint was a flat 1050px regardless of viewport, so mobile fetched the
  1920w variant for a ~423px real render. Replaced with a responsive sizes
  expression derived from the sibling backdrop image's own (already
  correct) hint, scaled by the callout's documented 125% overhang.
- cache-insight (best-fixable portion): _next/static/* filenames are
  content-hashed and immutable per deploy, but shared one cache rule with
  unhashed /public assets, capping both at 1-day max-age. Split into two
  rules - hashed assets now get 1-year immutable, unhashed assets keep the
  shorter revalidating TTL. Verified via a real build + server that both
  paths now return the correct distinct header.

Investigated and NOT changed (documented, not assumed):
- legacy-javascript-insight (14KB): traced via sourcemap to
  next/dist/build/polyfills/polyfill-module.js - Next's own built-in
  polyfill bundle, not our code or a dependency, and not exposed via any
  next.config.ts option. No browserslist misconfiguration on our end (none
  exists; Next already defaults to its modern target).
- forced-reflow-insight: even with source maps present locally, the
  dominant cost (335-417ms) stayed [unattributed] by Chrome's own profiler,
  and the small attributed slice was non-deterministic between our own
  chunk and a third-party script (HubSpot analytics) across runs - not a
  confident single root cause worth a targeted fix.
- render-blocking-insight / network-dependency-tree / bf-cache: bf-cache's
  actual failure reason is Cache-Control: no-store on the main document -
  the exact root cause already fixed on staging (PR simstudioai#5522/simstudioai#5528, the
  PublicEnvScript/unstable_noStore fix) but not yet promoted to main/prod.
  Resolves once that ships, not additional work here.

* fix(landing): convert mothership cover from PNG to JPEG (/blog LCP 6.6s -> 2.8s)

Ran a full Lighthouse sweep across every public page as requested. /blog
scored 73 (LCP 6.6s) while every other page scored 95+ - reproduced
consistently across 3 runs, not noise. Traced via lcp-breakdown-insight:
the LCP image (mothership/cover.png, 241KB even after the earlier palette
compression pass) took 6+ seconds to download on simulated mobile
throttling, well beyond what its size should cost.

PNG is a poor fit for this illustration's subtle gradients versus JPEG's
lossy compression. Verified empirically before converting: same 1920x1080
resolution, visually identical (spot-checked), 241KB -> 65KB (73% smaller).
No other cover in the content set uses PNG and benefits the same way
(checked copilot/cover.png, the only other PNG cover - already optimal at
64KB, converting it yielded no improvement, left unchanged).

Verified fix: /blog score 73->93, LCP 6.6s->2.8s, reproduced across 3 runs.

* fix(landing): correct mobile sizes tier, drop non-functional cache rule

- integrations-callout: account for FeatureCard's max-lg:grid-cols-1 mobile
  stack in the sizes hint, verified against Lighthouse's measured mobile
  render width.
- next.config: remove a custom _next/static cache-control rule that never
  actually fired (confirmed via header-marker test) - Next's own built-in
  default already applies the correct immutable 1yr cache to that path.

* fix(landing): correct sizes underestimate + fix dead .map header rule

- integrations-callout: derive sizes from the section's actual grid math
  (fixed 386px copy column, 40px gap, section gutters) instead of an
  approximated vw fraction. Verified against a static reproduction of the
  layout rendered at each Tailwind breakpoint - the old 110vw mobile tier
  underestimated real render width by ~3% right at the 1023px stack
  boundary, which could cause the browser to pick a too-small srcset
  candidate and upscale.
- next.config: the .map header rule's trailing `$` was read as a literal
  character by Next's path-to-regexp source matcher, not a regex anchor,
  so the rule never matched a real .map URL (confirmed via routes-manifest
  regex + a live header check). Removed the dead anchor and added a
  bounded Cache-Control so a future decision to stop shipping source maps
  isn't undermined by a 1yr immutable cache on already-fetched maps.

* fix(llms): serve well-formed llms.txt, remove Mothership + dead static files

Both the marketing site and docs site's llms.txt validator errors ("does
not appear to contain any links") traced to the same root cause: a static
public/llms.txt shadowed a better-written, already-existing dynamic
app/llms.txt route, and every "link" in the static files (and in the
docs app's auto-generated route) was bare `label: url` text, not Markdown
link syntax - so a strict Markdown-link parser found zero matches even
though URLs were visibly present.

- apps/sim: delete public/llms.txt (dead code, shadowing the properly
  Markdown-linked app/llms.txt route.ts, confirmed via production headers
  showing the static file was what actually served). Fix llms-full.txt's
  Links/Support/Legal sections to use [label](url) syntax, correct a
  stale "Next.js 15" reference, and replace "Mothership" with "Chat" per
  the constitution's language rules.
- apps/docs: same shadowing issue - delete the orphaned public/llms.txt
  (also still said "Mothership"). Fix the auto-generated per-page link
  list in app/llms.txt/route.ts to emit [title](url) instead of
  "title: url" for every documentation page.

* fix(llms): actually include the route.ts fixes from the prior commit

The prior commit (3b2d35c) only staged the two deleted public/llms.txt
files - these two modified route.ts files (the Mothership/link-format
fixes they were meant to accompany) were left unstaged. No new changes,
just completing that commit's intent.
… spinners (simstudioai#5612)

* improvement(chat): shimmer active subagent and tool labels instead of spinners

* improvement(chat): address review — focus-visible chevron, single shimmer source, reduced-motion rest color

* improvement(chat): pulse shimmer text under reduced motion so running state stays visible

* improvement(chat): reset background-clip in reduced-motion shimmer fallback

* fix(chat): apply reduced-motion shimmer fallback in dark mode too
…tudioai#5614)

* feat(sub-block): support multi-select in channel/user selector fields

* improvement(emcn): migrate Wizard primitive to ChipModal

* fix(emcn): wizard height sizes whole dialog; restore dialog description
…the form (simstudioai#5616)

* fix(demo): preload the Cal.com booking embed while the visitor fills the form

The embed script, booker iframe, and its assets only started downloading
after the visitor pressed Continue, so the calendar took several seconds
to appear. Warm the whole path on first form focus via the embed's
documented preload instruction (hidden ?preload=true iframe caches the
booker assets) plus a preconnect to app.cal.com. Nothing Cal.com-related
loads at initial page load, so Lighthouse/LCP are untouched.

* fix(demo): retry embed warm-up on failure, preconnect only on first focus

Reset the preload guard when embed.js fails to load so a later focus can
retry, and move the app.cal.com preconnect from render into the
focus-triggered preload path so initial page load makes zero Cal.com
connections.
…e it, on real browser payloads (simstudioai#5617)

* fix(rich-markdown-editor): make drag-reorder of an image actually move it, on real browser payloads

Reported on latest staging (deploy verified via CodePipeline): dragging an image duplicates it
instead of moving it, and a click with a few px of hand jitter — which the draggable <img> turns
into a native drag+drop-on-self — destroys the selection and duplicates too, reading as "I can't
select this image anymore". Reproduced in real Chromium with real mouse input (micro-drag becomes
dragstart, never click) and with the real drag payload shape.

Two compounding root causes, both empirically pinned:
- TipTap's node-view dragstart bypasses ProseMirror's drag serialization entirely (verified in
  @tiptap/core source: onDragStart only sets a drag image and NodeSelects the node — no PM
  text/html, no view.dragging). What the drop actually carries is the BROWSER's native enrichment:
  an image File plus text/html whose <img src> is the ABSOLUTE rendered URL.
- Both hosted-image recognizers (extractEmbeddedFileRef and isInlineRouteSrc) reject absolute
  URLs, so the simstudioai#5573 skip-check never matched on real drags: the drop fell into the upload branch
  (duplicate; original never moves). Falling through to PM instead would be no better: with
  view.dragging unset its default drop PARSES the html into a copy — persisting the display-layer
  src that share/export tracking don't recognize — and never deletes the original.

Fix, at the mechanism level:
- Normalize clipboard/dataTransfer srcs origin-relative before comparing (toSameOriginPath),
  keyed off window.location.origin deliberately rather than getBaseUrl(): the browser serializes
  against the origin the page is ACTUALLY viewed on, which legitimately diverges from the
  configured NEXT_PUBLIC_APP_URL (localhost dev, previews, apex-vs-www). Cross-origin srcs are
  never treated as ours. Applied to isInlineRouteSrc, hasHostedImageHtml, and findHostedImageAttrs
  (the paste-clone path had the same absolute-URL gap for browser-native "Copy Image").
- handleDrop performs the internal move itself when the drop's html references the
  currently-selected image node (htmlReferencesSrc — TipTap's dragstart guarantees that selection):
  same delete → map → insert shape as ProseMirror's own move, ending NodeSelected. Drop-on-self is
  a no-op that keeps the ring — which is what a jittery click now resolves to.

Empirical before/after (real-Chromium harness driving the real editor + engine): pre-fix the drag
leaves the original in place and uploads a duplicate; post-fix the node moves exactly once, nothing
uploads, and the moved image stays selected. Paste-clone verified for both relative (PM copy) and
absolute (native Copy Image) payloads. 604 unit tests pass including 11 new ones for the
origin-aware helpers.

* fix(rich-markdown-editor): no-op invalid drop points, match external-image identity by absolute URL

Greptile round 1, both real:
- dropPoint can return null (no valid insertion point); the raw coords.pos fallback could make
  tr.insert throw — PM's own null-fallback is only safe because it uses the forgiving
  replaceRangeWith. A null drop point is now a handled no-op: the node stays put, still selected.
- A doc image with a cross-origin src (README badge, CDN image) failed the same-origin identity
  check, so drag-reordering IT still fell into the duplicate path. htmlReferencesSrc now compares
  full ABSOLUTE URLs — identity is the question there, not hosted-by-us membership — while the
  hosted-recognition helpers stay same-origin-scoped. New regression test fails pre-fix.

Also folded the remaining inline comments into the handleDrop TSDoc and gave IMG_SRC_RE /
INLINE_ROUTE_QUERY_KEYS proper TSDoc (production diff is now TSDoc-only).
…ys, cmd-k speedups, pagespeed optimizations, security hardening
@utcarshsrivastava-collab

Copy link
Copy Markdown
Collaborator Author

Grill analysis complete — no open questions

v0.7.30 is a small slice. Merge is unblocked; the harness can proceed.

Conflict surface: 32 files. Upstream touched 112 files, the fork touched 1608 since the same
baseline (6c3d11b2); the intersection is the entire conflict set. Notably absent this run:

  • No packages/db/ changes — no migrations, no schema, no _journal.json
  • No package.json / bun.lock changes — lockfile bootstrap is a no-op
  • No tools/registry.ts / blocks/registry*.ts changes

Security fixes taken unconditionally: #5604 (mothership uploads previously needed only a
session, not workspace write/admin permission), #5601 (bounded form-data reads), #5599
(docx javascript: XSS), #5600 (chat-OTP authType re-check), #5613 (custom-block iconUrl).

Two calls resolved by inspection rather than asking:

  1. productionBrowserSourceMaps: true (#5605) — upstream's rationale is premised on the
    source already being public. Verified arenadeveloper02/p2-sim is PUBLIC, so the premise
    holds. Taken. If sourcemap build time / image size hurts the EC2 Docker pipeline, it's a
    one-line revert in apps/sim/next.config.ts.
  2. "Talk to sales" → /demo (#5602) — not a reroute for us. Those CTAs already point at
    /demo; upstream only replaces the literal with a shared constant. Taken.

Two things worth a human eye (neither blocks the merge):

  • Pre-existing, not sync-caused: demo-scheduler.tsx defaults CAL_LINK to
    'team/sim/demo'Sim's calendar. Set NEXT_PUBLIC_CAL_LINK before relying on the demo funnel.
  • lib/copilot/generated/ is upstreamFirst, but the fork hand-edited it. The Superagent
    task description carries a fork-only Google Docs GFM sentence that exists in no generator
    source
    , and bun run mship:generate can't regenerate here (scripts/sync-tool-catalog.ts
    reads a sibling ../copilot/ repo we don't have). A plain --theirs would silently delete it,
    so the plan re-applies it via mustEdit. Policy + extensibility notes updated so future syncs
    inherit this.

Plan: 5 child clusters — xai-byok-providers, api-routes-security,
copilot-payload-generated, workspace-ui, landing-branding-config.
Full reasoning in .upstream-sync/ledger/2026-08-06-2/run.md; work orders in merge-plan.draft.json.

One trap flagged for the children: on #5574 (xAI hosted keys) the fork is a strict superset
of upstream — it already has XAI_API_KEY+_1..3, the xai rotation branch, isXaiModel in
getApiKey's hosted gate, and xai in getHostedModels. Taking --theirs there would remove
fork capability. Only the new BYOK surface gets merged in.

utcarshsrivastava-collab added a commit that referenced this pull request Aug 6, 2026
Close the #686 duplicate path by seeding feat/github-merge-agent with #685's completed stack tip (v0.7.30 @ 207785c) so the next chained run stacks on that base.

Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants