Skip to content

Commit 2aaf2b7

Browse files
authored
v0.6.62: firecrawl parse, new gmail tools, trace improvements, tool fixes, db query optimizations, contract boundaries code hygiene, CORS, toast improvements, tables infinite query, executor robustness, reranker support
2 parents ecbf5e5 + db05297 commit 2aaf2b7

1,608 files changed

Lines changed: 75998 additions & 32776 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.agents/skills/add-integration/SKILL.md

Lines changed: 42 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -578,38 +578,64 @@ tools: {
578578

579579
#### 3. Create Internal API Route
580580

581-
Create `apps/sim/app/api/tools/{service}/{action}/route.ts`:
581+
Create `apps/sim/app/api/tools/{service}/{action}/route.ts`. Internal tool routes are HTTP boundaries and follow the same contract policy as public routes — define the request/response shape in `apps/sim/lib/api/contracts/{service}-tools.ts` (or an existing `internal-tools.ts` / `communication-tools.ts` aggregate) and validate with canonical helpers from `@/lib/api/server`. Never write a route-local Zod schema.
582582

583583
```typescript
584+
// apps/sim/lib/api/contracts/{service}-tools.ts
585+
import { z } from 'zod'
586+
import { defineRouteContract } from '@/lib/api/contracts'
587+
import { FileInputSchema } from '@/lib/uploads/utils/file-schemas'
588+
589+
export const {service}UploadBodySchema = z.object({
590+
accessToken: z.string(),
591+
file: FileInputSchema.optional().nullable(),
592+
fileContent: z.string().optional().nullable(),
593+
// ... other params
594+
})
595+
596+
export const {service}UploadResponseSchema = z.object({
597+
success: z.boolean(),
598+
output: z.object({ id: z.string(), url: z.string() }).optional(),
599+
error: z.string().optional(),
600+
})
601+
602+
export const {service}UploadContract = defineRouteContract({
603+
method: 'POST',
604+
path: '/api/tools/{service}/upload',
605+
body: {service}UploadBodySchema,
606+
response: { mode: 'json', schema: {service}UploadResponseSchema },
607+
})
608+
609+
export type {Service}UploadBody = z.input<typeof {service}UploadBodySchema>
610+
export type {Service}UploadResponse = z.output<typeof {service}UploadResponseSchema>
611+
```
612+
613+
```typescript
614+
// apps/sim/app/api/tools/{service}/upload/route.ts
584615
import { createLogger } from '@sim/logger'
585616
import { NextResponse, type NextRequest } from 'next/server'
586-
import { z } from 'zod'
617+
import { {service}UploadContract } from '@/lib/api/contracts/{service}-tools'
618+
import { parseRequest } from '@/lib/api/server'
587619
import { checkInternalAuth } from '@/lib/auth/hybrid'
588620
import { generateRequestId } from '@/lib/core/utils/request'
589-
import { FileInputSchema, type RawFileInput } from '@/lib/uploads/utils/file-schemas'
621+
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
622+
import { type RawFileInput } from '@/lib/uploads/utils/file-schemas'
590623
import { processFilesToUserFiles } from '@/lib/uploads/utils/file-utils'
591624
import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server'
592625

593626
const logger = createLogger('{Service}UploadAPI')
594627

595-
const RequestSchema = z.object({
596-
accessToken: z.string(),
597-
file: FileInputSchema.optional().nullable(),
598-
// Legacy field for backwards compatibility
599-
fileContent: z.string().optional().nullable(),
600-
// ... other params
601-
})
602-
603-
export async function POST(request: NextRequest) {
628+
export const POST = withRouteHandler(async (request: NextRequest) => {
604629
const requestId = generateRequestId()
605630

606631
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
607632
if (!authResult.success) {
608633
return NextResponse.json({ success: false, error: 'Unauthorized' }, { status: 401 })
609634
}
610635

611-
const body = await request.json()
612-
const data = RequestSchema.parse(body)
636+
const parsed = await parseRequest({service}UploadContract, request, {})
637+
if (!parsed.success) return parsed.response
638+
const data = parsed.data.body
613639

614640
let fileBuffer: Buffer
615641
let fileName: string
@@ -624,22 +650,20 @@ export async function POST(request: NextRequest) {
624650
fileBuffer = await downloadFileFromStorage(userFile, requestId, logger)
625651
fileName = userFile.name
626652
} else if (data.fileContent) {
627-
// Legacy: base64 string (backwards compatibility)
628653
fileBuffer = Buffer.from(data.fileContent, 'base64')
629654
fileName = 'file'
630655
} else {
631656
return NextResponse.json({ success: false, error: 'File required' }, { status: 400 })
632657
}
633658

634-
// Now call external API with fileBuffer
635659
const response = await fetch('https://api.{service}.com/upload', {
636660
method: 'POST',
637661
headers: { Authorization: `Bearer ${data.accessToken}` },
638-
body: new Uint8Array(fileBuffer), // Convert Buffer for fetch
662+
body: new Uint8Array(fileBuffer),
639663
})
640664

641665
// ... handle response
642-
}
666+
})
643667
```
644668

645669
#### 4. Update Tool to Use Internal Route

.agents/skills/cleanup/SKILL.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,8 @@ Run each of these skills in order on the specified scope, passing through the sc
2323
6. `/emcn-design-review $ARGUMENTS`
2424

2525
After all skills have run, output a summary of what was found and fixed (or proposed) across all six passes.
26+
27+
## Boundary Audit Guidance
28+
29+
- When removing route-local Zod schemas, replacing raw `fetch(` calls in hooks, or removing `as unknown as X` casts, do not introduce `// boundary-raw-fetch: <reason>` or `// double-cast-allowed: <reason>` annotations to silence the audit. Fix the underlying call instead — adopt a contract from `@/lib/api/contracts/**` and use `requestJson(contract, ...)` from `@/lib/api/client/request`, or refine the type so the double cast is unnecessary.
30+
- Annotations are reserved for legitimate exceptions only: streaming responses, binary downloads, multipart uploads, signed-URL flows, OAuth redirects, external-origin requests, and double casts where no narrower type is available. Each annotation requires a non-empty reason; empty reasons fail `bun run check:api-validation:strict`.

.agents/skills/ship/SKILL.md

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,20 @@ When the user runs `/ship`:
1313

1414
1. **Check git status** - See what files have changed
1515
2. **Generate a commit message** following this format: `type(scope): description`
16-
- Types: `fix`, `feat`, `improvement`, `chore`
17-
- Scope: short identifier (e.g., `undo-redo`, `api`, `ui`)
18-
- Keep it concise
19-
20-
3. **Run lint** - Run `bun run lint` from the repo root to fix formatting issues before staging
21-
16+
- Types: `fix`, `feat`, `improvement`, `chore`
17+
- Scope: short identifier (e.g., `undo-redo`, `api`, `ui`)
18+
- Keep it concise
19+
3. **Run pre-ship checks** from the repo root before staging:
20+
- `bun run lint` to fix formatting issues
21+
- `bun run check:api-validation:strict` to catch boundary contract failures before CI
2222
4. **Stage and commit** the changes with the generated message
23-
2423
5. **Push to origin** using the current branch name
25-
2624
6. **Create a PR** to staging with a description in the user's voice
2725

2826
## Commit Message Format
2927

3028
Based on the repo's commit history:
29+
3130
```
3231
fix(scope): description for bug fixes
3332
feat(scope): description for new features
@@ -61,6 +60,7 @@ Tested manually (or describe testing)
6160
## PR Creation Command
6261

6362
Use this command structure:
63+
6464
```bash
6565
gh pr create --base staging --title "COMMIT_MESSAGE" --body "PR_BODY"
6666
```
@@ -77,6 +77,7 @@ gh pr create --base staging --title "COMMIT_MESSAGE" --body "PR_BODY"
7777

7878
- Short, direct bullet points
7979
- No unnecessary explanation
80-
- "Tested manually" is acceptable for testing section
80+
- "Tested manually" is acceptable for testing section; include lint and boundary validation results when run
8181
- Checkboxes filled in appropriately
8282
- No screenshots section unless UI changes
83+

.claude/commands/ship.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ When the user runs `/ship`:
1717
- Scope: short identifier (e.g., `undo-redo`, `api`, `ui`)
1818
- Keep it concise
1919

20-
3. **Run lint** - Run `bun run lint` from the repo root to fix formatting issues before staging
20+
3. **Run pre-ship checks** from the repo root before staging:
21+
- `bun run lint` to fix formatting issues
22+
- `bun run check:api-validation:strict` to catch boundary contract failures before CI
2123

2224
4. **Stage and commit** the changes with the generated message
2325

@@ -77,6 +79,6 @@ gh pr create --base staging --title "COMMIT_MESSAGE" --body "PR_BODY"
7779

7880
- Short, direct bullet points
7981
- No unnecessary explanation
80-
- "Tested manually" is acceptable for testing section
82+
- "Tested manually" is acceptable for testing section; include lint and boundary validation results when run
8183
- Checkboxes filled in appropriately
8284
- No screenshots section unless UI changes

.cursor/commands/ship.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ When the user runs `/ship`:
1212
- Scope: short identifier (e.g., `undo-redo`, `api`, `ui`)
1313
- Keep it concise
1414

15-
3. **Run lint** - Run `bun run lint` from the repo root to fix formatting issues before staging
15+
3. **Run pre-ship checks** from the repo root before staging:
16+
- `bun run lint` to fix formatting issues
17+
- `bun run check:api-validation:strict` to catch boundary contract failures before CI
1618

1719
4. **Stage and commit** the changes with the generated message
1820

@@ -72,6 +74,6 @@ gh pr create --base staging --title "COMMIT_MESSAGE" --body "PR_BODY"
7274

7375
- Short, direct bullet points
7476
- No unnecessary explanation
75-
- "Tested manually" is acceptable for testing section
77+
- "Tested manually" is acceptable for testing section; include lint and boundary validation results when run
7678
- Checkboxes filled in appropriately
7779
- No screenshots section unless UI changes

.cursor/rules/sim-architecture.mdc

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,16 @@ feature/
5454
- **Create `utils.ts` when** 2+ files need the same helper
5555
- **Check existing sources** before duplicating (`lib/` has many utilities)
5656
- **Location**: `lib/` (app-wide) → `feature/utils/` (feature-scoped) → inline (single-use)
57+
58+
## API Contracts
59+
60+
Boundary HTTP request and response shapes for all routes under `apps/sim/app/api/**` live in `apps/sim/lib/api/contracts/` (one file per resource family). Routes and clients consume the same contract — routes never define route-local boundary Zod schemas, and clients never define ad-hoc wire types. Domain validators that are not HTTP boundaries (tools, blocks, triggers, connectors, realtime handlers, internal helpers) may still use Zod directly; the contract rule is boundary-only.
61+
62+
- Each contract is built with `defineRouteContract({ method, path, params?, query?, body?, headers?, response: { mode: 'json', schema } })` and exports both schemas and named TypeScript type aliases (e.g., `export type CreateFolderBody = z.input<typeof createFolderBodySchema>`).
63+
- Shared identifier schemas live in `apps/sim/lib/api/contracts/primitives.ts`.
64+
- Routes validate via canonical helpers in `apps/sim/lib/api/server/validation.ts` (`parseRequest`, `validationErrorResponse`, `getValidationErrorMessage`, `isZodError`). Routes never `import { z } from 'zod'` and never use `instanceof z.ZodError`.
65+
- Clients call `requestJson(contract, ...)` from `apps/sim/lib/api/client/request.ts`; hooks import named type aliases from contracts, never `z.input/z.output`.
66+
- Routes under `apps/sim/app/api/v1/**` use `apps/sim/app/api/v1/middleware.ts` for shared auth, rate-limit, and workspace access. Compose contract validation inside that middleware.
67+
- `bun run check:api-validation` enforces this policy and must pass on PRs.
68+
69+
`bun run check:api-validation:strict` is the strict CI gate and additionally fails on annotations with empty reasons. Four per-line opt-out forms are recognized: `// boundary-raw-fetch: <reason>` (placed immediately above a legitimate raw `fetch(` call in `apps/sim/hooks/queries/**` or `apps/sim/hooks/selectors/**` for stream/binary/multipart/signed-URL/OAuth-redirect/external-origin cases), `// double-cast-allowed: <reason>` (placed immediately above an `as unknown as X` cast outside test files), `// boundary-raw-json: <reason>` (placed immediately above a raw `await request.json()` / `await req.json()` read in a route handler that cannot go through `parseRequest` — JSON-RPC envelopes, tolerant `.catch(() => ({}))` parses), and `// untyped-response: <reason>` (placed immediately above a `schema: z.unknown()` response declaration in a contract file when the response body is genuinely opaque). The reason must be non-empty. Whole-file allowlists for routes that legitimately import Zod for non-boundary reasons go through `INDIRECT_ZOD_ROUTES` in `scripts/check-api-validation-contracts.ts`, not per-line annotations.

.cursor/rules/sim-queries.mdc

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,18 @@ Never use inline query keys — always use the factory.
3737
- Every `queryFn` must destructure and forward `signal` for request cancellation
3838
- Every query must have an explicit `staleTime`
3939
- Use `keepPreviousData` only on variable-key queries (where params change), never on static keys
40+
- Same-origin JSON calls must go through `requestJson(contract, ...)` from `@/lib/api/client/request` against the contract in `@/lib/api/contracts/**`
4041

4142
```typescript
42-
async function fetchEntities(workspaceId: string, signal?: AbortSignal) {
43-
const response = await fetch(`/api/entities?workspaceId=${workspaceId}`, { signal })
44-
if (!response.ok) throw new Error('Failed to fetch entities')
45-
return response.json()
43+
import { requestJson } from '@/lib/api/client/request'
44+
import { listEntitiesContract, type EntityList } from '@/lib/api/contracts/entities'
45+
46+
async function fetchEntities(workspaceId: string, signal?: AbortSignal): Promise<EntityList> {
47+
const data = await requestJson(listEntitiesContract, {
48+
query: { workspaceId },
49+
signal,
50+
})
51+
return data.entities
4652
}
4753

4854
export function useEntityList(workspaceId?: string, options?: { enabled?: boolean }) {
@@ -51,7 +57,7 @@ export function useEntityList(workspaceId?: string, options?: { enabled?: boolea
5157
queryFn: ({ signal }) => fetchEntities(workspaceId as string, signal),
5258
enabled: Boolean(workspaceId) && (options?.enabled ?? true),
5359
staleTime: 60 * 1000,
54-
placeholderData: keepPreviousData, // OK: workspaceId varies
60+
placeholderData: keepPreviousData,
5561
})
5662
}
5763
```
@@ -118,6 +124,12 @@ const handler = useCallback(() => {
118124
}, [data])
119125
```
120126

127+
## Boundary Types
128+
129+
- Hooks must import named type aliases from `@/lib/api/contracts/**` (e.g., `import { listEntitiesContract, type EntityList } from '@/lib/api/contracts/entities'`). Never write `z.input<...>` or `z.output<...>` in hooks.
130+
- Hooks must not `import { z } from 'zod'`. Boundary types come from contract aliases; non-boundary helpers can stay in plain TypeScript.
131+
- For non-contract endpoints (multipart uploads, binary downloads, streaming responses, signed-URL flows, OAuth redirects, external origins), it is OK to keep raw `fetch`. Each legitimate raw `fetch(` call inside `apps/sim/hooks/queries/**` or `apps/sim/hooks/selectors/**` must be preceded by a `// boundary-raw-fetch: <reason>` annotation on the immediately preceding line (up to three non-empty preceding comment lines are tolerated). The reason must be non-empty — empty reasons fail strict mode. The audit script `scripts/check-api-validation-contracts.ts` (`bun run check:api-validation` / `bun run check:api-validation:strict`) enforces this.
132+
121133
## Naming
122134

123135
- **Keys**: `entityKeys`

.devcontainer/docker-compose.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ services:
2121
- COPILOT_API_KEY=${COPILOT_API_KEY}
2222
- SIM_AGENT_API_URL=${SIM_AGENT_API_URL}
2323
- OLLAMA_URL=${OLLAMA_URL:-http://localhost:11434}
24-
- NEXT_PUBLIC_SOCKET_URL=${NEXT_PUBLIC_SOCKET_URL:-http://localhost:3002}
24+
- NEXT_PUBLIC_SOCKET_URL=${NEXT_PUBLIC_SOCKET_URL:-}
2525
- BUN_INSTALL_CACHE_DIR=/home/bun/.bun/cache
2626
depends_on:
2727
db:

.github/workflows/test-build.yml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,22 +90,25 @@ jobs:
9090
9191
echo "✅ All feature flags are properly configured"
9292
93-
- name: Check subblock ID stability
93+
- name: Check block registry invariants
9494
run: |
9595
if [ "${{ github.event_name }}" = "pull_request" ]; then
9696
BASE_REF="origin/${{ github.base_ref }}"
9797
git fetch --depth=1 origin "${{ github.base_ref }}" 2>/dev/null || true
9898
else
9999
BASE_REF="HEAD~1"
100100
fi
101-
bun run apps/sim/scripts/check-subblock-id-stability.ts "$BASE_REF"
101+
bun run apps/sim/scripts/check-block-registry.ts "$BASE_REF"
102102
103103
- name: Lint code
104104
run: bun run lint:check
105105

106106
- name: Enforce monorepo boundaries
107107
run: bun run check:boundaries
108108

109+
- name: API contract boundary audit
110+
run: bun run check:api-validation:strict
111+
109112
- name: Verify realtime prune graph
110113
run: bun run check:realtime-prune
111114

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,4 @@ i18n.cache
8080
## Claude Code
8181
.claude/launch.json
8282
.claude/worktrees/
83+
.claude/scheduled_tasks.lock

0 commit comments

Comments
 (0)