Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .claude/rules/emcn-components.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Import components, `cn`, and tokens from the `@sim/emcn` barrel; icons come from

Never hand-roll the chip pill from raw class strings (they go stale). Compose from the canonical sources:

- **Surface, typography + content tokens:** `chip/chip-chrome.ts` — `chipFilledSurfaceTokens`, `chipFieldSurfaceClass`, `chipFieldTextClass` (text fields and the dropdown search box build on these), plus the chip-content chrome `chipContentGap`, `chipGeometryClass`, `chipContentIconClass`, `chipContentLabelClass`, `cellIconNodeClass` (non-chip surfaces that must visually match chip content, e.g. resource table cells), and the row-state pair `chipHoverSurfaceClass` / `chipActiveSurfaceClass` (hover vs. selected — mutually exclusive, so a selected row holds its surface through hover; every hand-rolled row imports these rather than restating the literals). All are re-exported from the `@sim/emcn` barrel — no subpath import needed.
- **Surface, typography + content tokens:** `chip/chip-chrome.ts` — `chipFieldSurfaceClass`, `chipFieldTextClass` (text fields and the dropdown search box build on these), plus the chip-content chrome `chipContentGap`, `chipGeometryClass`, `chipContentIconClass`, `chipContentLabelClass`, `cellIconNodeClass` (non-chip surfaces that must visually match chip content, e.g. resource table cells), and the row-state pair `chipHoverSurfaceClass` / `chipActiveSurfaceClass` (hover vs. selected — mutually exclusive, so a selected row holds its surface through hover; every hand-rolled row imports these rather than restating the literals). All are re-exported from the `@sim/emcn` barrel — no subpath import needed.
- **Pill geometry:** `chip/chip.tsx` — `chipVariants` (30px tall, `rounded-lg`, `px-2`, icon↔text `gap-1.5`). Every pill-shaped trigger (`ChipDropdown`, `ChipSelect`, `ChipSwitch`) reuses it for visual parity.

Canonical look: normal font-weight (never `font-medium`/`font-semibold`), value text `--text-body`, icons `--text-icon` at `size-[14px]`, placeholder `--text-muted`, `transition-colors`, **no focus ring** (the caret marks focus). Filled surface is `--surface-5` light / `--surface-4` dark with a `--border` border (`chip-chrome.ts` still spells it through the legacy alias `--border-1`; new code writes `--border`).
Expand Down Expand Up @@ -54,6 +54,6 @@ Declare keyboard intent on the action-owning primitive; never add document-level
- **Discriminated-union props for modes** (e.g. `multiple`, the modal field `type`) instead of near-duplicate components.
- **Delete legacy variants after migration** — leave no dead paths.
- **Verify CSS vars exist.** An undefined var resolves to `currentColor`, which draws black borders. Align to the canonical tokens: normal weight, `--text-body`, `--text-icon`.
- Use Radix UI primitives for accessibility. Export the component and its `variants` (when using CVA). Document with TSDoc + a usage example.
- Use Radix UI primitives for accessibility. Export the component; export its CVA `variants` once another module composes them (`check:unused-exports` flags an export nothing imports). Document with TSDoc + a usage example.

Color tokens and icon-size conventions are canonical in `.claude/rules/sim-styling.md` — follow it rather than restating.
4 changes: 2 additions & 2 deletions .cursor/rules/emcn-components.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Import components, `cn`, and tokens from the `@sim/emcn` barrel; icons come from

Never hand-roll the chip pill from raw class strings (they go stale). Compose from the canonical sources:

- **Surface, typography + content tokens:** `chip/chip-chrome.ts` — `chipFilledSurfaceTokens`, `chipFieldSurfaceClass`, `chipFieldTextClass` (text fields and the dropdown search box build on these), plus the chip-content chrome `chipContentGap`, `chipGeometryClass`, `chipContentIconClass`, `chipContentLabelClass`, `cellIconNodeClass` (non-chip surfaces that must visually match chip content, e.g. resource table cells), and the row-state pair `chipHoverSurfaceClass` / `chipActiveSurfaceClass` (hover vs. selected — mutually exclusive, so a selected row holds its surface through hover; every hand-rolled row imports these rather than restating the literals). All are re-exported from the `@sim/emcn` barrel — no subpath import needed.
- **Surface, typography + content tokens:** `chip/chip-chrome.ts` — `chipFieldSurfaceClass`, `chipFieldTextClass` (text fields and the dropdown search box build on these), plus the chip-content chrome `chipContentGap`, `chipGeometryClass`, `chipContentIconClass`, `chipContentLabelClass`, `cellIconNodeClass` (non-chip surfaces that must visually match chip content, e.g. resource table cells), and the row-state pair `chipHoverSurfaceClass` / `chipActiveSurfaceClass` (hover vs. selected — mutually exclusive, so a selected row holds its surface through hover; every hand-rolled row imports these rather than restating the literals). All are re-exported from the `@sim/emcn` barrel — no subpath import needed.
- **Pill geometry:** `chip/chip.tsx` — `chipVariants` (30px tall, `rounded-lg`, `px-2`, icon↔text `gap-1.5`). Every pill-shaped trigger (`ChipDropdown`, `ChipSelect`, `ChipSwitch`) reuses it for visual parity.

Canonical look: normal font-weight (never `font-medium`/`font-semibold`), value text `--text-body`, icons `--text-icon` at `size-[14px]`, placeholder `--text-muted`, `transition-colors`, **no focus ring** (the caret marks focus). Filled surface is `--surface-5` light / `--surface-4` dark with a `--border` border (`chip-chrome.ts` still spells it through the legacy alias `--border-1`; new code writes `--border`).
Expand Down Expand Up @@ -55,6 +55,6 @@ Declare keyboard intent on the action-owning primitive; never add document-level
- **Discriminated-union props for modes** (e.g. `multiple`, the modal field `type`) instead of near-duplicate components.
- **Delete legacy variants after migration** — leave no dead paths.
- **Verify CSS vars exist.** An undefined var resolves to `currentColor`, which draws black borders. Align to the canonical tokens: normal weight, `--text-body`, `--text-icon`.
- Use Radix UI primitives for accessibility. Export the component and its `variants` (when using CVA). Document with TSDoc + a usage example.
- Use Radix UI primitives for accessibility. Export the component; export its CVA `variants` once another module composes them (`check:unused-exports` flags an export nothing imports). Document with TSDoc + a usage example.

Color tokens and icon-size conventions are canonical in `.claude/rules/sim-styling.md` — follow it rather than restating.
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ This file (also `AGENTS.md`) holds the repo-wide rules. Area detail lives in `.c
- **Logging**: `createLogger` from `@sim/logger`; `logger.info` / `logger.warn` / `logger.error`, never `console.log`. Inside `withRouteHandler` the logger already carries the request ID — no manual `withMetadata({ requestId })`.
- **Comments**: name things so the code explains itself. TSDoc documents exported APIs and non-obvious modules. An inline `//` is only for a terse, non-obvious *why*, or for a script-enforced `// <tag>: <reason>` annotation (`boundary-raw-fetch`, `double-cast-allowed`, `boundary-raw-json`, `untyped-response`, `rq-lint-allow`, `client-boundary-allow`, `utils-lint-allow`, …). History belongs in the commit message. No `====` separators or commented-out code (`check:comment-hygiene` enforces this). The `/you-might-not-need-a-comment` skill applies this to a diff.
- **ID generation**: `generateId()` (UUID v4, the default) or `generateShortId(size?)` (URL-safe, 21 chars by default) from `@sim/utils/id` — never `crypto.randomUUID()`, `nanoid`, or `uuid`. Both use `crypto.getRandomValues()`, so they also work in non-secure (HTTP) browsers. For other randomness, `@sim/utils/random` (`randomInt`, `randomFloat`, `randomItem`, `generateRandomBytes`, `generateRandomHex`) — never `Math.random()` or `crypto.randomBytes()`.
- **Common utilities**: use the shared helpers from `@sim/utils` instead of inline implementations (`check:utils` bans most of the inline forms below):
- **Common utilities**: use the shared helpers from the `@sim/utils/<module>` subpaths instead of inline implementations (`check:utils` bans most of the inline forms below):
- `sleep(ms)` from `@sim/utils/helpers` — never `new Promise(resolve => setTimeout(resolve, ms))`
- `toError(e)` from `@sim/utils/errors` — normalize caught values to `Error`; never `e instanceof Error ? e : new Error(String(e))`
- `getErrorMessage(e, fallback?)` from `@sim/utils/errors` — never `e instanceof Error ? e.message : 'fallback'`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ vi.mock('@/lib/mcp/application/use-cases', () => mcpUseCasesMock)
vi.mock('@/lib/workspaces/application/workspace-context', () => workspaceContextMock)
vi.mock('@/lib/permission-groups/config-scope.server', () => permissionGroupScopeMock)
vi.mock('@/lib/workflows/application/context', () => workflowContextMock)
vi.mock('@sim/workflow-persistence', () => ({
vi.mock('@sim/workflow-persistence/load', () => ({
loadWorkflowFromNormalizedTablesRaw: hoisted.loadWorkflow,
}))

Expand Down
2 changes: 1 addition & 1 deletion apps/sim/lib/mcp/application/execute-managed-tool.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const hoisted = vi.hoisted(() => ({
saveToolSnapshot: vi.fn(),
}))

vi.mock('@sim/workflow-persistence', () => ({
vi.mock('@sim/workflow-persistence/load', () => ({
loadWorkflowFromNormalizedTablesRaw: hoisted.loadWorkflow,
}))

Expand Down
2 changes: 1 addition & 1 deletion apps/sim/lib/mcp/application/execute-tool.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const hoisted = vi.hoisted(() => ({
getServer: vi.fn(),
}))

vi.mock('@sim/workflow-persistence', () => ({
vi.mock('@sim/workflow-persistence/load', () => ({
loadWorkflowFromNormalizedTablesRaw: hoisted.loadWorkflow,
}))

Expand Down
4 changes: 3 additions & 1 deletion apps/sim/lib/mcp/application/operation-access.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import { queueTableRows, resetDbChainMock, schemaMock } from '@sim/testing'
import { beforeEach, describe, expect, it, vi } from 'vitest'

const { loadWorkflow } = vi.hoisted(() => ({ loadWorkflow: vi.fn() }))
vi.mock('@sim/workflow-persistence', () => ({ loadWorkflowFromNormalizedTablesRaw: loadWorkflow }))
vi.mock('@sim/workflow-persistence/load', () => ({
loadWorkflowFromNormalizedTablesRaw: loadWorkflow,
}))

import {
loadMcpOperationAccess,
Expand Down
2 changes: 1 addition & 1 deletion apps/sim/lib/mcp/application/operation-access.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { Principal } from '@sim/auth/principal'
import { db } from '@sim/db'
import { workflowDeploymentVersion } from '@sim/db/schema'
import { isPlainRecord } from '@sim/utils/object'
import { loadWorkflowFromNormalizedTablesRaw } from '@sim/workflow-persistence'
import { loadWorkflowFromNormalizedTablesRaw } from '@sim/workflow-persistence/load'
import { and, eq } from 'drizzle-orm'
import { OrchestrationError } from '@/lib/core/orchestration/types'
import {
Expand Down
2 changes: 1 addition & 1 deletion apps/sim/stores/workflows/workflow/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export type {
Variable,
WorkflowState,
}
export { isValidSubflowType, SUBFLOW_TYPES } from '@sim/workflow-types/workflow'
export { SUBFLOW_TYPES } from '@sim/workflow-types/workflow'

export interface WorkflowActions {
updateNodeDimensions: (id: string, dimensions: { width: number; height: number }) => void
Expand Down
30 changes: 20 additions & 10 deletions knip.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,22 @@
// Unused exports, types, and duplicates are ratcheted by check:unused-exports,
// which reuses this config in the same knip pass.
"include": ["files", "dependencies", "unlisted", "unresolved"],
// knip's default, stated so it is a decision: no entry file's exports are reported.
// That keeps package `exports`/`main`/`bin` contracts (ts-sdk, emcn, cli, …) and framework
// entries (Next routes, Trigger tasks) public, and also exempts the other configured entries
// (scripts, `*.integration.ts`, `background/**`, desktop and SDK examples).
"includeEntryExports": false,
// On by default: every workspace's `exports` map serves only this monorepo, so an entry export
// no workspace imports is dead. The workspaces below turn it off because their entries are
// public or standalone: the apps (framework entries such as Next routes and Trigger tasks), the
// published packages (cli, sim-cli, sim-setup, ts-sdk), db (standalone scripts), and the root
// (scripts). A new private package is checked without opting in.
"includeEntryExports": true,
"workspaces": {
".": {
"includeEntryExports": false,
"entry": ["scripts/**/*.{ts,tsx}", "vitest.shared.ts"],
"paths": { "@/*": ["./apps/sim/*"] },
// This alias selects the native tsc binary; see check-native-typecheck.ts.
"ignoreDependencies": ["@typescript/native"]
},
"apps/sim": {
"includeEntryExports": false,
"entry": [
// Container bootstrap and Trigger's directory-discovered task graph.
"bootstrap.ts",
Expand Down Expand Up @@ -69,8 +72,9 @@
// Live test spawns this path with cwd=MSHIP_WORKER_ROOT (a sibling repo).
"ignoreUnresolved": ["tools/probes/src/controller-run-read.ts"]
},
"apps/realtime": { "entry": ["src/bootstrap.ts"] },
"apps/realtime": { "includeEntryExports": false, "entry": ["src/bootstrap.ts"] },
"apps/desktop": {
"includeEntryExports": false,
// scripts/build.ts and e2e/updater.spec.ts supply these to esbuild by path.
"entry": [
"src/main/index.ts",
Expand All @@ -83,24 +87,30 @@
// ensure-pty-prebuilds.ts assembles these package names for universal builds.
"ignoreDependencies": ["@lydell/node-pty-darwin-arm64", "@lydell/node-pty-darwin-x64"]
},
"apps/docs": { "includeEntryExports": false },
"apps/pii": { "includeEntryExports": false },
"packages/cli": { "includeEntryExports": false },
"packages/db": {
"includeEntryExports": false,
"entry": [
"scripts/*.ts",
"script-migrations/*.ts",
"**/*.integration.ts",
"vitest.integration.setup.ts"
]
},
"packages/sim-cli": {
"entry": ["src/index.ts", "src/runtime.ts", "scripts/*.ts"]
},
"packages/ts-sdk": { "entry": ["examples/*.ts"] },
// The contract audit reads this snapshot by filename.
// Generated, so its export surface is not ratcheted either.
"packages/desktop-bridge": {
"entry": ["contract-snapshot.ts"],
"ignoreIssues": { "contract-snapshot.ts": ["exports", "types", "duplicates"] }
},
"packages/sim-cli": {
"includeEntryExports": false,
"entry": ["src/index.ts", "src/runtime.ts", "scripts/*.ts"]
},
"packages/sim-setup": { "includeEntryExports": false },
"packages/ts-sdk": { "includeEntryExports": false, "entry": ["examples/*.ts"] },
// This shared config is consumed by apps that own the Next dependency.
"packages/tsconfig": { "ignoreUnresolved": ["next"] }
}
Expand Down
13 changes: 5 additions & 8 deletions packages/auth/src/principal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export type Principal =
| SlackAppPrincipal

/** Verified app-wide ingress authority; installation lookup grants no human access. */
export interface SlackAppPrincipal {
interface SlackAppPrincipal {
kind: 'slack_app'
appId: string
appRevision: string
Expand Down Expand Up @@ -94,7 +94,7 @@ export interface ExternalUserSubject {
}

/** Email address proven by a deployment's OTP or SSO authentication gate. */
export interface AuthenticatedEmailSubject {
interface AuthenticatedEmailSubject {
kind: 'authenticated_email'
email: string
}
Expand All @@ -106,7 +106,7 @@ interface ActorlessSystemPrincipal {
workflowId: string
}

export interface ChatSystemPrincipal {
interface ChatSystemPrincipal {
kind: 'system'
serviceId: 'chat'
workspaceId: string
Expand All @@ -124,10 +124,7 @@ export interface WebhookSystemPrincipal {
subject?: ExternalUserSubject
}

export type SystemPrincipal =
| ActorlessSystemPrincipal
| ChatSystemPrincipal
| WebhookSystemPrincipal
type SystemPrincipal = ActorlessSystemPrincipal | ChatSystemPrincipal | WebhookSystemPrincipal

interface DelegatedPrincipalBase {
kind: 'delegated'
Expand All @@ -153,7 +150,7 @@ export interface SubjectDelegatedPrincipal extends DelegatedPrincipalBase {
subjectUserId: string
}

export interface WorkflowExecutionDelegationContext {
interface WorkflowExecutionDelegationContext {
kind: 'workflow_execution'
workflowId: string
executionId?: string
Expand Down
2 changes: 1 addition & 1 deletion packages/auth/src/verify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export interface VerifyAuthOptions {
* created by `apps/sim`'s full auth config, so it can carry plugin fields
* (e.g. `activeOrganizationId`) this minimal instance does not configure.
*/
export interface VerifiedOneTimeTokenSession {
interface VerifiedOneTimeTokenSession {
user: {
id: string
name: string | null
Expand Down
17 changes: 7 additions & 10 deletions packages/browser-protocol/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,23 +57,20 @@ export const CURRENT_BROWSER_TOOL_NAMES = [

export type CurrentBrowserToolName = (typeof CURRENT_BROWSER_TOOL_NAMES)[number]

export const RETIRED_BROWSER_TOOL_NAMES = ['browser_request_takeover'] as const
const RETIRED_BROWSER_TOOL_NAMES = ['browser_request_takeover'] as const

export const BROWSER_TOOL_NAMES = [
...CURRENT_BROWSER_TOOL_NAMES,
...RETIRED_BROWSER_TOOL_NAMES,
] as const
const BROWSER_TOOL_NAMES = [...CURRENT_BROWSER_TOOL_NAMES, ...RETIRED_BROWSER_TOOL_NAMES] as const

export type BrowserToolName = (typeof BROWSER_TOOL_NAMES)[number]

export const BROWSER_WAIT_FOR_DEFAULT_TIMEOUT_MS = 10_000
export const BROWSER_WAIT_FOR_MAX_TIMEOUT_MS = 120_000
export const BROWSER_WAIT_FOR_RENDERER_GRACE_MS = 15_000
const BROWSER_WAIT_FOR_DEFAULT_TIMEOUT_MS = 10_000
const BROWSER_WAIT_FOR_MAX_TIMEOUT_MS = 120_000
const BROWSER_WAIT_FOR_RENDERER_GRACE_MS = 15_000
export const BROWSER_TOOL_AUTHORIZATION_TIMEOUT_MS = 8_000
export const BROWSER_NAVIGATION_NATIVE_WATCHDOG_MS = 60_000
export const BROWSER_TOOL_QUEUE_WAIT_TIMEOUT_MS = BROWSER_NAVIGATION_NATIVE_WATCHDOG_MS
const BROWSER_RENDERER_TRANSPORT_GRACE_MS = 2_000
export const BROWSER_NAVIGATION_RENDERER_TIMEOUT_MS =
const BROWSER_NAVIGATION_RENDERER_TIMEOUT_MS =
BROWSER_TOOL_AUTHORIZATION_TIMEOUT_MS +
BROWSER_TOOL_QUEUE_WAIT_TIMEOUT_MS +
BROWSER_NAVIGATION_NATIVE_WATCHDOG_MS +
Expand Down Expand Up @@ -132,7 +129,7 @@ export function browserToolRendererTimeoutMs(
}
}

export const BROWSER_THEMES = ['system', 'light', 'dark'] as const
const BROWSER_THEMES = ['system', 'light', 'dark'] as const

/** Sim appearance preference mirrored into browser-tab media queries. */
export type BrowserTheme = (typeof BROWSER_THEMES)[number]
Expand Down
Loading
Loading