Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
84 commits
Select commit Hold shift + click to select a range
2d2448f
fix build error
icecrasher321 Apr 3, 2026
1fc84b8
improvement(mothership): new agent loop (#3920)
Sg312 Apr 4, 2026
e2de4d2
Force redeploy
Sg312 Apr 4, 2026
b6e1df4
feat(motheship): add docx support
Sg312 Apr 4, 2026
acc00df
feat(mothership): append
Sg312 Apr 4, 2026
cac100a
Add deps
Sg312 Apr 4, 2026
0d09d11
improvement(mothership): docs
Sg312 Apr 4, 2026
0b3f3ed
File types
Sg312 Apr 5, 2026
0a41b8b
Add client retry logic
Sg312 Apr 5, 2026
d85775e
Fix stream reconnect
Sg312 Apr 5, 2026
b74cf28
Eager tool streaming
Sg312 Apr 6, 2026
87ff68c
Fix client side tools
Sg312 Apr 6, 2026
4ee6fa8
Security
Sg312 Apr 6, 2026
33433b1
Fix shell var injection
Sg312 Apr 6, 2026
ca2afaa
Remove auto injected tasks
Sg312 Apr 6, 2026
8dfbe8a
Fix 10mb tool response limit
Sg312 Apr 6, 2026
c29941e
Fix trailing leak
Sg312 Apr 6, 2026
2da0cbe
Remove dead tools
Sg312 Apr 7, 2026
c52d633
file/folder tools
Sg312 Apr 7, 2026
0dd1ee0
Folder tools
Sg312 Apr 7, 2026
d25632c
Hide function code inline
Sg312 Apr 7, 2026
5c47c1f
Dont show internal tool result reads
Sg312 Apr 7, 2026
ebc030b
Fix spacing
Sg312 Apr 7, 2026
c31ae46
Auth vfs
Sg312 Apr 7, 2026
a6fbb51
Empty folders should show in vfs
Sg312 Apr 7, 2026
1a18ebb
Fix run workflow
Sg312 Apr 7, 2026
674695e
change to node runtime
icecrasher321 Apr 7, 2026
75d5d13
revert back to bun runtime
icecrasher321 Apr 7, 2026
ed2dad0
Fix
Sg312 Apr 7, 2026
2b799f3
Appends
Sg312 Apr 8, 2026
5b22f1f
Remove debug logs
Sg312 Apr 8, 2026
5be55d2
Patch
Sg312 Apr 8, 2026
89f8842
Fix patch tool
Sg312 Apr 8, 2026
3893afd
Temp
Sg312 Apr 8, 2026
ce1f00c
Checkpoint
Sg312 Apr 8, 2026
fd4fa1c
File writes
Sg312 Apr 8, 2026
d22f367
Fix
Sg312 Apr 9, 2026
b49d67e
Remove tool truncation limits
Sg312 Apr 9, 2026
f2fcfe7
Bad hook
Sg312 Apr 9, 2026
817833c
replace react markdown with streamdown
icecrasher321 Apr 9, 2026
2ba4228
Checkpoitn
Sg312 Apr 9, 2026
81ac66f
fix code block
icecrasher321 Apr 9, 2026
2abf6ac
Merge branch 'dev' of github.com:simstudioai/sim into dev
icecrasher321 Apr 9, 2026
a738a6d
fix stream persistence
icecrasher321 Apr 9, 2026
69d69ee
temp
Sg312 Apr 9, 2026
d25c243
Fix file tools
Sg312 Apr 9, 2026
6f04c48
tool joining
Sg312 Apr 9, 2026
b1caeb0
cleanup subagent + streaming issues
icecrasher321 Apr 9, 2026
c77f204
streamed text change
icecrasher321 Apr 9, 2026
e610df6
Merge branch 'dev' of github.com:simstudioai/sim into dev
icecrasher321 Apr 9, 2026
fe5baf7
Tool display intetns
Sg312 Apr 9, 2026
f0d3819
Fix dev
Sg312 Apr 9, 2026
0638604
Fix tests
Sg312 Apr 9, 2026
9da574a
Fix dev
Sg312 Apr 9, 2026
649ee9c
Speed up dev ci
Sg312 Apr 9, 2026
3ef87e5
Add req id
Sg312 Apr 9, 2026
2d2f782
Fix persistence
Sg312 Apr 9, 2026
485dce7
Tool call names
Sg312 Apr 10, 2026
f509e33
fix payload accesses
icecrasher321 Apr 10, 2026
e321e99
Merge branch 'dev' of github.com:simstudioai/sim into dev
icecrasher321 Apr 10, 2026
8f3c8e4
Fix name
Sg312 Apr 10, 2026
7835df4
fix snapshot crash bug
icecrasher321 Apr 10, 2026
24abd87
Merge branch 'dev' of github.com:simstudioai/sim into dev
icecrasher321 Apr 10, 2026
9272b15
fix
Sg312 Apr 10, 2026
3252736
Fix
Sg312 Apr 10, 2026
c61cbb0
Merge branch 'staging' into dev
icecrasher321 Apr 10, 2026
33d1342
remove worker code
icecrasher321 Apr 10, 2026
c026ce7
Clickable resources
Sg312 Apr 10, 2026
5b94db6
Options ordering
Sg312 Apr 10, 2026
2156f49
Folder vfs
Sg312 Apr 10, 2026
c74c4a9
Restore and mass delete tools
Sg312 Apr 10, 2026
ca361a3
Fix
Sg312 Apr 10, 2026
949601c
lint
icecrasher321 Apr 10, 2026
91301df
Update request tracing and skills and handlers
Sg312 Apr 10, 2026
734a4d1
Fix editable
Sg312 Apr 10, 2026
e2b4eb3
fix type error
icecrasher321 Apr 10, 2026
386d0aa
Html code
Sg312 Apr 10, 2026
3690dc2
fix(chat): make inline code inherit parent font size in markdown headers
waleedlatif1 Apr 10, 2026
da28f8a
improved autolayout
icecrasher321 Apr 10, 2026
ac84c62
Merge branch 'dev' of github.com:simstudioai/sim into dev
icecrasher321 Apr 10, 2026
09f06cb
durable stream for files
icecrasher321 Apr 10, 2026
d11d3df
one more fix
icecrasher321 Apr 10, 2026
22cd5e4
POSSIBLE BREAKAGE: SCROLLING
Sg312 Apr 10, 2026
0f6c16d
Fixes
Sg312 Apr 10, 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
  •  
  •  
  •  
80 changes: 69 additions & 11 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ permissions:
jobs:
test-build:
name: Test and Build
if: github.ref != 'refs/heads/dev' || github.event_name == 'pull_request'
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Dev branch skips CI tests before building and deploying

Medium Severity

The test-build job is skipped for pushes to the dev branch (line 19), and build-dev only depends on detect-version, not test-build. Combined with migrate-dev running migrations after the build, this means untested code gets built into Docker images and database migrations are applied to the dev environment without any test validation.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 949601c. Configure here.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Bugbot Autofix determined this is a false positive.

This is intentional design: PRs to dev still run tests (via the pull_request condition), only direct pushes to dev skip tests for faster iteration cycles in the dev environment.

This Bugbot Autofix run was free. To enable autofix for future PRs, go to the Cursor dashboard.

uses: ./.github/workflows/test-build.yml
secrets: inherit

Expand Down Expand Up @@ -45,11 +46,66 @@ jobs:
echo "ℹ️ Not a release commit"
fi

# Build AMD64 images and push to ECR immediately (+ GHCR for main)
# Dev: build all 3 images for ECR only (no GHCR, no ARM64)
build-dev:
name: Build Dev ECR
needs: [detect-version]
if: github.event_name == 'push' && github.ref == 'refs/heads/dev'
runs-on: blacksmith-8vcpu-ubuntu-2404
permissions:
contents: read
id-token: write
strategy:
fail-fast: false
matrix:
include:
- dockerfile: ./docker/app.Dockerfile
ecr_repo_secret: ECR_APP
- dockerfile: ./docker/db.Dockerfile
ecr_repo_secret: ECR_MIGRATIONS
- dockerfile: ./docker/realtime.Dockerfile
ecr_repo_secret: ECR_REALTIME
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.DEV_AWS_ROLE_TO_ASSUME }}
aws-region: ${{ secrets.DEV_AWS_REGION }}

- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2

- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Set up Docker Buildx
uses: useblacksmith/setup-docker-builder@v1

- name: Build and push
uses: useblacksmith/build-push-action@v2
with:
context: .
file: ${{ matrix.dockerfile }}
platforms: linux/amd64
push: true
tags: ${{ steps.login-ecr.outputs.registry }}/${{ secrets[matrix.ecr_repo_secret] }}:dev
provenance: false
sbom: false

# Main/staging: build AMD64 images and push to ECR + GHCR
build-amd64:
name: Build AMD64
needs: [detect-version]
if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/staging' || github.ref == 'refs/heads/dev')
needs: [test-build, detect-version]
if: >-
github.event_name == 'push' &&
(github.ref == 'refs/heads/main' || github.ref == 'refs/heads/staging')
runs-on: blacksmith-8vcpu-ubuntu-2404
permissions:
contents: read
Expand All @@ -75,8 +131,8 @@ jobs:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ github.ref == 'refs/heads/main' && secrets.AWS_ROLE_TO_ASSUME || github.ref == 'refs/heads/dev' && secrets.DEV_AWS_ROLE_TO_ASSUME || secrets.STAGING_AWS_ROLE_TO_ASSUME }}
aws-region: ${{ github.ref == 'refs/heads/main' && secrets.AWS_REGION || github.ref == 'refs/heads/dev' && secrets.DEV_AWS_REGION || secrets.STAGING_AWS_REGION }}
role-to-assume: ${{ github.ref == 'refs/heads/main' && secrets.AWS_ROLE_TO_ASSUME || secrets.STAGING_AWS_ROLE_TO_ASSUME }}
aws-region: ${{ github.ref == 'refs/heads/main' && secrets.AWS_REGION || secrets.STAGING_AWS_REGION }}

- name: Login to Amazon ECR
id: login-ecr
Expand Down Expand Up @@ -106,26 +162,20 @@ jobs:
ECR_REPO="${{ secrets[matrix.ecr_repo_secret] }}"
GHCR_IMAGE="${{ matrix.ghcr_image }}"

# ECR tags (always build for ECR)
if [ "${{ github.ref }}" = "refs/heads/main" ]; then
ECR_TAG="latest"
elif [ "${{ github.ref }}" = "refs/heads/dev" ]; then
ECR_TAG="dev"
else
ECR_TAG="staging"
fi
ECR_IMAGE="${ECR_REGISTRY}/${ECR_REPO}:${ECR_TAG}"

# Build tags list
TAGS="${ECR_IMAGE}"

# Add GHCR tags only for main branch
if [ "${{ github.ref }}" = "refs/heads/main" ]; then
GHCR_AMD64="${GHCR_IMAGE}:latest-amd64"
GHCR_SHA="${GHCR_IMAGE}:${{ github.sha }}-amd64"
TAGS="${TAGS},$GHCR_AMD64,$GHCR_SHA"

# Add version tag if this is a release commit
if [ "${{ needs.detect-version.outputs.is_release }}" = "true" ]; then
VERSION="${{ needs.detect-version.outputs.version }}"
GHCR_VERSION="${GHCR_IMAGE}:${VERSION}-amd64"
Expand Down Expand Up @@ -256,6 +306,14 @@ jobs:
docker manifest push "${IMAGE_BASE}:${VERSION}"
fi

# Run database migrations for dev
migrate-dev:
name: Migrate Dev DB
needs: [build-dev]
if: github.event_name == 'push' && github.ref == 'refs/heads/dev'
uses: ./.github/workflows/migrations.yml
secrets: inherit

# Check if docs changed
check-docs-changes:
name: Check Docs Changes
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/migrations.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,5 @@ jobs:
- name: Apply migrations
working-directory: ./packages/db
env:
DATABASE_URL: ${{ github.ref == 'refs/heads/main' && secrets.DATABASE_URL || secrets.STAGING_DATABASE_URL }}
DATABASE_URL: ${{ github.ref == 'refs/heads/main' && secrets.DATABASE_URL || github.ref == 'refs/heads/dev' && secrets.DEV_DATABASE_URL || secrets.STAGING_DATABASE_URL }}
run: bunx drizzle-kit migrate --config=./drizzle.config.ts
10 changes: 2 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,6 @@ docker compose -f docker-compose.prod.yml up -d

Open [http://localhost:3000](http://localhost:3000)

#### Background worker note

The Docker Compose stack starts a dedicated worker container by default. If `REDIS_URL` is not configured, the worker will start, log that it is idle, and do no queue processing. This is expected. Queue-backed API, webhook, and schedule execution requires Redis; installs without Redis continue to use the inline execution path.

Sim also supports local models via [Ollama](https://ollama.ai) and [vLLM](https://docs.vllm.ai/) — see the [Docker self-hosting docs](https://docs.sim.ai/self-hosting/docker) for setup details.

### Self-hosted: Manual Setup
Expand Down Expand Up @@ -123,12 +119,10 @@ cd packages/db && bun run db:migrate
5. Start development servers:

```bash
bun run dev:full # Starts Next.js app, realtime socket server, and the BullMQ worker
bun run dev:full # Starts Next.js app and realtime socket server
```

If `REDIS_URL` is not configured, the worker will remain idle and execution continues inline.

Or run separately: `bun run dev` (Next.js), `cd apps/sim && bun run dev:sockets` (realtime), and `cd apps/sim && bun run worker` (BullMQ worker).
Or run separately: `bun run dev` (Next.js) and `cd apps/sim && bun run dev:sockets` (realtime).

## Copilot API Keys

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

import { type SVGProps, useEffect, useRef, useState } from 'react'
import { AnimatePresence, motion, useInView } from 'framer-motion'
import ReactMarkdown, { type Components } from 'react-markdown'
import remarkGfm from 'remark-gfm'
import { Streamdown } from 'streamdown'
import 'streamdown/styles.css'
import { ChevronDown } from '@/components/emcn'
import { Database, File, Library, Table } from '@/components/emcn/icons'
import {
Expand Down Expand Up @@ -557,26 +557,32 @@ The team agreed to prioritize the new onboarding flow. Key decisions:

Follow up with engineering on the timeline for the API v2 migration. Draft the proposal for the board meeting next week.`

const MD_COMPONENTS: Components = {
h1: ({ children }) => (
const MD_COMPONENTS = {
h1: ({ children }: { children?: React.ReactNode }) => (
<p
role='presentation'
className='mb-4 border-[#E5E5E5] border-b pb-2 font-semibold text-[#1C1C1C] text-[20px]'
>
{children}
</p>
),
h2: ({ children }) => (
h2: ({ children }: { children?: React.ReactNode }) => (
<h2 className='mt-5 mb-3 border-[#E5E5E5] border-b pb-1.5 font-semibold text-[#1C1C1C] text-[16px]'>
{children}
</h2>
),
ul: ({ children }) => <ul className='mb-3 list-disc pl-6'>{children}</ul>,
ol: ({ children }) => <ol className='mb-3 list-decimal pl-6'>{children}</ol>,
li: ({ children }) => (
ul: ({ children }: { children?: React.ReactNode }) => (
<ul className='mb-3 list-disc pl-6'>{children}</ul>
),
ol: ({ children }: { children?: React.ReactNode }) => (
<ol className='mb-3 list-decimal pl-6'>{children}</ol>
),
li: ({ children }: { children?: React.ReactNode }) => (
<li className='mb-1 text-[#1C1C1C] text-[14px] leading-[1.6]'>{children}</li>
),
p: ({ children }) => <p className='mb-3 text-[#1C1C1C] text-[14px] leading-[1.6]'>{children}</p>,
p: ({ children }: { children?: React.ReactNode }) => (
<p className='mb-3 text-[#1C1C1C] text-[14px] leading-[1.6]'>{children}</p>
),
}

function MockFullFiles() {
Expand Down Expand Up @@ -618,9 +624,9 @@ function MockFullFiles() {
transition={{ duration: 0.4, delay: 0.5 }}
>
<div className='h-full overflow-auto p-6'>
<ReactMarkdown remarkPlugins={[remarkGfm]} components={MD_COMPONENTS}>
<Streamdown mode='static' components={MD_COMPONENTS}>
{source}
</ReactMarkdown>
</Streamdown>
</div>
</motion.div>
</div>
Expand Down
144 changes: 144 additions & 0 deletions apps/sim/app/api/admin/mothership/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import { db } from '@sim/db'
import { user } from '@sim/db/schema'
import { eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { getSession } from '@/lib/auth'
import { env } from '@/lib/core/config/env'

const ENV_URLS: Record<string, string | undefined> = {
dev: env.MOTHERSHIP_DEV_URL,
staging: env.MOTHERSHIP_STAGING_URL,
prod: env.MOTHERSHIP_PROD_URL,
}

function getMothershipUrl(environment: string): string | null {
return ENV_URLS[environment] ?? null
}

async function isAdminRequestAuthorized() {
const session = await getSession()
if (!session?.user?.id) return false

const [currentUser] = await db
.select({ role: user.role })
.from(user)
.where(eq(user.id, session.user.id))
.limit(1)

return currentUser?.role === 'admin'
}

/**
* Proxy to the mothership admin API.
*
* Query params:
* env - "dev" | "staging" | "prod"
* endpoint - the admin endpoint path, e.g. "requests", "licenses", "traces"
*
* The request body (for POST) is forwarded as-is. Additional query params
* (e.g. requestId for GET /traces) are forwarded.
*/
export async function POST(req: NextRequest) {
if (!(await isAdminRequestAuthorized())) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}

const adminKey = env.MOTHERSHIP_API_ADMIN_KEY
if (!adminKey) {
return NextResponse.json({ error: 'MOTHERSHIP_API_ADMIN_KEY not configured' }, { status: 500 })
}

const { searchParams } = new URL(req.url)
const environment = searchParams.get('env') || 'dev'
const endpoint = searchParams.get('endpoint')

if (!endpoint) {
return NextResponse.json({ error: 'endpoint query param required' }, { status: 400 })
}

const baseUrl = getMothershipUrl(environment)
if (!baseUrl) {
return NextResponse.json(
{ error: `No URL configured for environment: ${environment}` },
{ status: 400 }
)
}

const targetUrl = `${baseUrl}/api/admin/${endpoint}`
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Endpoint parameter allows path traversal in proxy URL

Medium Severity

The endpoint query parameter is interpolated directly into the target URL (${baseUrl}/api/admin/${endpoint}) without any sanitization or validation. A value like ../../other-path would resolve to ${baseUrl}/other-path, allowing an admin user to reach arbitrary paths on the mothership server beyond /api/admin/. The same issue exists in both the POST and GET handlers.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 949601c. Configure here.


try {
const body = await req.text()
const upstream = await fetch(targetUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': adminKey,
},
...(body ? { body } : {}),
})

const data = await upstream.json()
return NextResponse.json(data, { status: upstream.status })
} catch (error) {
return NextResponse.json(
{
error: `Failed to reach mothership (${environment}): ${error instanceof Error ? error.message : 'Unknown error'}`,
},
{ status: 502 }
)
}
}

export async function GET(req: NextRequest) {
if (!(await isAdminRequestAuthorized())) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}

const adminKey = env.MOTHERSHIP_API_ADMIN_KEY
if (!adminKey) {
return NextResponse.json({ error: 'MOTHERSHIP_API_ADMIN_KEY not configured' }, { status: 500 })
}

const { searchParams } = new URL(req.url)
const environment = searchParams.get('env') || 'dev'
const endpoint = searchParams.get('endpoint')

if (!endpoint) {
return NextResponse.json({ error: 'endpoint query param required' }, { status: 400 })
}

const baseUrl = getMothershipUrl(environment)
if (!baseUrl) {
return NextResponse.json(
{ error: `No URL configured for environment: ${environment}` },
{ status: 400 }
)
}

const forwardParams = new URLSearchParams()
searchParams.forEach((value, key) => {
if (key !== 'env' && key !== 'endpoint') {
forwardParams.set(key, value)
}
})

const qs = forwardParams.toString()
const targetUrl = `${baseUrl}/api/admin/${endpoint}${qs ? `?${qs}` : ''}`

try {
const upstream = await fetch(targetUrl, {
method: 'GET',
headers: { 'x-api-key': adminKey },
})

const data = await upstream.json()
return NextResponse.json(data, { status: upstream.status })
} catch (error) {
return NextResponse.json(
{
error: `Failed to reach mothership (${environment}): ${error instanceof Error ? error.message : 'Unknown error'}`,
},
{ status: 502 }
)
}
}
2 changes: 1 addition & 1 deletion apps/sim/app/api/billing/update-cost/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { recordUsage } from '@/lib/billing/core/usage-log'
import { checkAndBillOverageThreshold } from '@/lib/billing/threshold-billing'
import { checkInternalApiKey } from '@/lib/copilot/utils'
import { checkInternalApiKey } from '@/lib/copilot/request/http'
import { isBillingEnabled } from '@/lib/core/config/feature-flags'
import { generateRequestId } from '@/lib/core/utils/request'

Expand Down
Loading
Loading