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
60 changes: 60 additions & 0 deletions .claude/commands/add-block-preview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
---
description: Gate a block's visibility — ship an unreleased block as a preview (hidden until revealed via AppConfig/env), reveal it to admins/orgs, GA it, or kill-switch a shipped block
argument-hint: <block-type>
---

# Add Block Preview Skill

You manage **block visibility gating** in Sim — hiding blocks from every discovery surface (toolbar, cmd+K search, copilot @-mentions, agent tool picker, mothership VFS/metadata/tools, Access Control list, public docs/catalog) while **never** gating execution of already-placed instances.

## The model

Three levers, evaluated in `apps/sim/lib/core/config/block-visibility.ts` and folded into the registry accessors (`apps/sim/blocks/registry.ts`):

1. **`preview: true`** on the `BlockConfig` (static, in code) — the block is default-hidden EVERYWHERE (hosted, self-hosted, dev, SSR) until revealed. Fail-closed.
2. **The hosted `block-visibility` AppConfig document** — per-block rule keyed by the existing block type:

```jsonc
{
"<block-type>": {
"enabled": false, // required. true = GA (visible to everyone)
"orgIds": ["org_..."], // optional allowlist clauses (any match reveals)
"userIds": ["user_..."],
"adminEnabled": true // platform admins (user.role === 'admin')
}
}
```

3. **`PREVIEW_BLOCKS` env** (comma-separated block types) — the off-AppConfig reveal path for self-hosters and local dev.

A revealed block that is not globally GA (`enabled !== true`, or env-revealed) renders with a **" (Preview)"** name suffix on discovery surfaces. `getBlock()` stays pure, so placed instances keep their canonical name and always execute.

## Lifecycle of a preview block

1. **Author** the block normally (`/add-block` etc.) and set `preview: true` on its `BlockConfig`. **Ship no `BlockMeta` and no docs until GA** — `check-block-registry` deliberately skips preview blocks in meta coverage, and `generate-docs` skips them at every gate.
2. **Local dev:** set `PREVIEW_BLOCKS=<block-type>` in your env to see it (with the suffix).
3. **Merge/deploy.** The block's code is live everywhere but visible nowhere — no AppConfig rule exists and self-hosters have no env entry.
4. **Hosted preview:** add a rule to the `block-visibility` AppConfig document and start a deployment (no code deploy):
- Admins only: `{ "enabled": false, "adminEnabled": true }`
- Design-partner org: `{ "enabled": false, "orgIds": ["org_123"] }`
- GA via config (code cleanup pending): `{ "enabled": true }` — suffix disappears everywhere within ~30s (AppConfig TTL) + client refetch.

Same runbook as `feature-flags`: edit the hosted document, `aws appconfig start-deployment` with the `sim-<env>-fast` strategy (see the infra README).
5. **GA cleanup:** delete `preview: true` from the block (now visible to self-hosters on their next upgrade), add its `BlockMeta` + regen docs, and drop the AppConfig entry. For a v2 upgrade, this is also when v1 gets `hideFromToolbar: true` (the superseded-version paradigm).

## Kill switch (shipped blocks)

To pull an already-GA block from discovery surfaces on hosted (incident, deprecation): add `{ "<block-type>": { "enabled": false } }` to the document. Allowlist clauses can carve out exceptions. **Execution is NOT stopped** — workflows already using the block keep running; the kill switch only prevents new placement/discovery.

## Invariants (do not violate)

- **Execution is never gated.** The executor, serializer, drop-naming, and `isBlockTypeAccessControlExempt` resolve via pure `getBlock`. Do not add visibility checks to execution paths.
- **Clone-not-remove:** gated blocks stay in `getAllBlocks()` output as clones with `hideFromToolbar: true` — `.find`-by-type consumers rely on this. Never filter them out.
- **Keys are registry block types.** Never `custom_block_*` (parse drops them — custom blocks have their own enabled/disabled lifecycle).
- **The shared hidden-predicate is `isHiddenUnder`** (`apps/sim/blocks/visibility/context.ts`). Never restate the preview/disabled rule inline at a new consumer.
- **Process-global caches stay ungated.** `getStaticComponentFiles` (VFS) and `getExposedIntegrationTools` build the ungated universe; per-viewer filtering happens at stamp/consumer time. Never move gating into a shared builder.
- Gating is **surface hiding, not secrecy** — the full config ships in the client JS bundle. Anything truly secret cannot be a registered block.

## Tests

Evaluation semantics: `apps/sim/lib/core/config/block-visibility.test.ts`. Registry projection: `apps/sim/blocks/visibility/visibility.test.ts`. When gating behavior changes, extend those — mock `isPlatformAdmin` for the admin clause; use the local `withAppConfig` harness.
60 changes: 60 additions & 0 deletions .cursor/commands/add-block-preview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
---
description: Gate a block's visibility — ship an unreleased block as a preview (hidden until revealed via AppConfig/env), reveal it to admins/orgs, GA it, or kill-switch a shipped block
argument-hint: <block-type>
---

# Add Block Preview Skill

You manage **block visibility gating** in Sim — hiding blocks from every discovery surface (toolbar, cmd+K search, copilot @-mentions, agent tool picker, mothership VFS/metadata/tools, Access Control list, public docs/catalog) while **never** gating execution of already-placed instances.

## The model

Three levers, evaluated in `apps/sim/lib/core/config/block-visibility.ts` and folded into the registry accessors (`apps/sim/blocks/registry.ts`):

1. **`preview: true`** on the `BlockConfig` (static, in code) — the block is default-hidden EVERYWHERE (hosted, self-hosted, dev, SSR) until revealed. Fail-closed.
2. **The hosted `block-visibility` AppConfig document** — per-block rule keyed by the existing block type:

```jsonc
{
"<block-type>": {
"enabled": false, // required. true = GA (visible to everyone)
"orgIds": ["org_..."], // optional allowlist clauses (any match reveals)
"userIds": ["user_..."],
"adminEnabled": true // platform admins (user.role === 'admin')
}
}
```

3. **`PREVIEW_BLOCKS` env** (comma-separated block types) — the off-AppConfig reveal path for self-hosters and local dev.

A revealed block that is not globally GA (`enabled !== true`, or env-revealed) renders with a **" (Preview)"** name suffix on discovery surfaces. `getBlock()` stays pure, so placed instances keep their canonical name and always execute.

## Lifecycle of a preview block

1. **Author** the block normally (`/add-block` etc.) and set `preview: true` on its `BlockConfig`. **Ship no `BlockMeta` and no docs until GA** — `check-block-registry` deliberately skips preview blocks in meta coverage, and `generate-docs` skips them at every gate.
2. **Local dev:** set `PREVIEW_BLOCKS=<block-type>` in your env to see it (with the suffix).
3. **Merge/deploy.** The block's code is live everywhere but visible nowhere — no AppConfig rule exists and self-hosters have no env entry.
4. **Hosted preview:** add a rule to the `block-visibility` AppConfig document and start a deployment (no code deploy):
- Admins only: `{ "enabled": false, "adminEnabled": true }`
- Design-partner org: `{ "enabled": false, "orgIds": ["org_123"] }`
- GA via config (code cleanup pending): `{ "enabled": true }` — suffix disappears everywhere within ~30s (AppConfig TTL) + client refetch.

Same runbook as `feature-flags`: edit the hosted document, `aws appconfig start-deployment` with the `sim-<env>-fast` strategy (see the infra README).
5. **GA cleanup:** delete `preview: true` from the block (now visible to self-hosters on their next upgrade), add its `BlockMeta` + regen docs, and drop the AppConfig entry. For a v2 upgrade, this is also when v1 gets `hideFromToolbar: true` (the superseded-version paradigm).

## Kill switch (shipped blocks)

To pull an already-GA block from discovery surfaces on hosted (incident, deprecation): add `{ "<block-type>": { "enabled": false } }` to the document. Allowlist clauses can carve out exceptions. **Execution is NOT stopped** — workflows already using the block keep running; the kill switch only prevents new placement/discovery.

## Invariants (do not violate)

- **Execution is never gated.** The executor, serializer, drop-naming, and `isBlockTypeAccessControlExempt` resolve via pure `getBlock`. Do not add visibility checks to execution paths.
- **Clone-not-remove:** gated blocks stay in `getAllBlocks()` output as clones with `hideFromToolbar: true` — `.find`-by-type consumers rely on this. Never filter them out.
- **Keys are registry block types.** Never `custom_block_*` (parse drops them — custom blocks have their own enabled/disabled lifecycle).
- **The shared hidden-predicate is `isHiddenUnder`** (`apps/sim/blocks/visibility/context.ts`). Never restate the preview/disabled rule inline at a new consumer.
- **Process-global caches stay ungated.** `getStaticComponentFiles` (VFS) and `getExposedIntegrationTools` build the ungated universe; per-viewer filtering happens at stamp/consumer time. Never move gating into a shared builder.
- Gating is **surface hiding, not secrecy** — the full config ships in the client JS bundle. Anything truly secret cannot be a registered block.

## Tests

Evaluation semantics: `apps/sim/lib/core/config/block-visibility.test.ts`. Registry projection: `apps/sim/blocks/visibility/visibility.test.ts`. When gating behavior changes, extend those — mock `isPlatformAdmin` for the admin clause; use the local `withAppConfig` harness.
77 changes: 77 additions & 0 deletions apps/docs/components/icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3687,6 +3687,62 @@ export const SakanaIcon = (props: SVGProps<SVGSVGElement>) => (
</svg>
)

export function MetaIcon(props: SVGProps<SVGSVGElement>) {
const id = useId()
const gradient1Id = `meta_gradient_1_${id}`
const gradient2Id = `meta_gradient_2_${id}`

return (
<svg
{...props}
height='1em'
viewBox='0 -48.28 287.56 287.56'
width='1em'
xmlns='http://www.w3.org/2000/svg'
>
<title>Meta</title>
<defs>
<linearGradient
id={gradient1Id}
x1='61'
x2='259'
y1='117'
y2='127'
gradientUnits='userSpaceOnUse'
>
<stop offset='0' stopColor='#0064e1' />
<stop offset='0.4' stopColor='#0064e1' />
<stop offset='0.83' stopColor='#0073ee' />
<stop offset='1' stopColor='#0082fb' />
</linearGradient>
<linearGradient
id={gradient2Id}
x1='45'
x2='45'
y1='139'
y2='66'
gradientUnits='userSpaceOnUse'
>
<stop offset='0' stopColor='#0082fb' />
<stop offset='1' stopColor='#0064e0' />
</linearGradient>
</defs>
<path
d='m31.06,125.96c0,10.98 2.41,19.41 5.56,24.51 4.13,6.68 10.29,9.51 16.57,9.51 8.1,0 15.51-2.01 29.79-21.76 11.44-15.83 24.92-38.05 33.99-51.98l15.36-23.6c10.67-16.39 23.02-34.61 37.18-46.96 11.56-10.08 24.03-15.68 36.58-15.68 21.07,0 41.14,12.21 56.5,35.11 16.81,25.08 24.97,56.67 24.97,89.27 0,19.38-3.82,33.62-10.32,44.87-6.28,10.88-18.52,21.75-39.11,21.75l0-31.02c17.63,0 22.03-16.2 22.03-34.74 0-26.42-6.16-55.74-19.73-76.69-9.63-14.86-22.11-23.94-35.84-23.94-14.85,0-26.8,11.2-40.23,31.17-7.14,10.61-14.47,23.54-22.7,38.13l-9.06,16.05c-18.2,32.27-22.81,39.62-31.91,51.75-15.95,21.24-29.57,29.29-47.5,29.29-21.27,0-34.72-9.21-43.05-23.09-6.8-11.31-10.14-26.15-10.14-43.06z'
fill='#0081fb'
/>
<path
d='m24.49,37.3c14.24-21.95 34.79-37.3 58.36-37.3 13.65,0 27.22,4.04 41.39,15.61 15.5,12.65 32.02,33.48 52.63,67.81l7.39,12.32c17.84,29.72 27.99,45.01 33.93,52.22 7.64,9.26 12.99,12.02 19.94,12.02 17.63,0 22.03-16.2 22.03-34.74l27.4-.86c0,19.38-3.82,33.62-10.32,44.87-6.28,10.88-18.52,21.75-39.11,21.75-12.8,0-24.14-2.78-36.68-14.61-9.64-9.08-20.91-25.21-29.58-39.71l-25.79-43.08c-12.94-21.62-24.81-37.74-31.68-45.04-7.39-7.85-16.89-17.33-32.05-17.33-12.27,0-22.69,8.61-31.41,21.78z'
fill={`url(#${gradient1Id})`}
/>
<path
d='m82.35,31.23c-12.27,0-22.69,8.61-31.41,21.78-12.33,18.61-19.88,46.33-19.88,72.95 0,10.98 2.41,19.41 5.56,24.51l-26.48,17.44c-6.8-11.31-10.14-26.15-10.14-43.06 0-30.75 8.44-62.8 24.49-87.55 14.24-21.95 34.79-37.3 58.36-37.3z'
fill={`url(#${gradient2Id})`}
/>
</svg>
)
}

export function GeminiIcon(props: SVGProps<SVGSVGElement>) {
const id = useId()
const gradientId = `gemini_gradient_${id}`
Expand Down Expand Up @@ -5572,6 +5628,27 @@ export function SshIcon(props: SVGProps<SVGSVGElement>) {
)
}

export function SshTerminalIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg
{...props}
xmlns='http://www.w3.org/2000/svg'
width='16'
height='14'
viewBox='0 0 16 14'
fill='none'
>
<path
d='M3 0C1.34 0 0 1.34 0 3V11C0 12.66 1.34 14 3 14H13C14.66 14 16 12.66 16 11V3C16 1.34 14.66 0 13 0H3ZM1 3C1 1.9 1.9 1 3 1H13C14.1 1 15 1.9 15 3V4H1V3ZM1 5H15V11C15 12.1 14.1 13 13 13H3C1.9 13 1 12.1 1 11V5Z'
fill='currentColor'
/>
<circle cx='3.5' cy='2.5' r='0.75' fill='currentColor' />
<circle cx='5.75' cy='2.5' r='0.75' fill='currentColor' />
<circle cx='8' cy='2.5' r='0.75' fill='currentColor' />
</svg>
)
}

export function SftpIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg
Expand Down
9 changes: 6 additions & 3 deletions apps/docs/content/docs/en/integrations/ashby.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -1109,6 +1109,7 @@ Trigger workflow when a new application is submitted
| ↳ `currentInterviewStage` | object | currentInterviewStage output from the tool |
| ↳ `id` | string | Current interview stage UUID |
| ↳ `title` | string | Current interview stage title |
| ↳ `stageType` | string | Current interview stage type \(e.g., Lead, Applied, Interview, Offer\) |
| ↳ `job` | object | job output from the tool |
| ↳ `id` | string | Job UUID |
| ↳ `title` | string | Job title |
Expand Down Expand Up @@ -1163,6 +1164,7 @@ Trigger workflow when a candidate is hired
| ↳ `currentInterviewStage` | object | currentInterviewStage output from the tool |
| ↳ `id` | string | Current interview stage UUID |
| ↳ `title` | string | Current interview stage title |
| ↳ `stageType` | string | Current interview stage type \(e.g., Lead, Applied, Interview, Offer\) |
| ↳ `job` | object | job output from the tool |
| ↳ `id` | string | Job UUID |
| ↳ `title` | string | Job title |
Expand Down Expand Up @@ -1204,6 +1206,7 @@ Trigger workflow when a candidate changes interview stages
| ↳ `currentInterviewStage` | object | currentInterviewStage output from the tool |
| ↳ `id` | string | Current interview stage UUID |
| ↳ `title` | string | Current interview stage title |
| ↳ `stageType` | string | Current interview stage type \(e.g., Lead, Applied, Interview, Offer\) |
| ↳ `job` | object | job output from the tool |
| ↳ `id` | string | Job UUID |
| ↳ `title` | string | Job title |
Expand Down Expand Up @@ -1231,7 +1234,7 @@ Trigger workflow when a new job is created
| ↳ `title` | string | Job title |
| ↳ `confidential` | boolean | Whether the job is confidential |
| ↳ `status` | string | Job status \(Open, Closed, Draft, Archived\) |
| ↳ `employmentType` | string | Employment type \(Full-time, Part-time, etc.\) |
| ↳ `employmentType` | string | Employment type \(FullTime, PartTime, Intern, Contract\) |


---
Expand All @@ -1254,8 +1257,8 @@ Trigger workflow when a new offer is created
| `offer` | object | offer output from the tool |
| ↳ `id` | string | Offer UUID |
| ↳ `applicationId` | string | Associated application UUID |
| ↳ `acceptanceStatus` | string | Offer acceptance status \(Accepted, Declined, Pending, Created, Cancelled, WaitingOnResponse\) |
| ↳ `offerStatus` | string | Offer process status \(WaitingOnApprovalStart, WaitingOnOfferApproval, WaitingOnCandidateResponse, CandidateAccepted, CandidateRejected, OfferCancelled\) |
| ↳ `acceptanceStatus` | string | Offer acceptance status \(Accepted, Declined, Pending, Created, Cancelled\) |
| ↳ `offerStatus` | string | Offer process status \(WaitingOnApprovalStart, WaitingOnOfferApproval, WaitingOnApprovalDefinition, WaitingOnCandidateResponse, CandidateRejected, CandidateAccepted, OfferCancelled\) |
| ↳ `decidedAt` | string | Offer decision timestamp \(ISO 8601\). Typically null at creation; populated after candidate responds. |
| ↳ `latestVersion` | object | latestVersion output from the tool |
| ↳ `id` | string | Latest offer version UUID |
Expand Down
4 changes: 3 additions & 1 deletion apps/docs/content/docs/en/integrations/gitlab.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -882,6 +882,7 @@ Trigger workflow when an issue is opened, updated, or closed in GitLab
| ↳ `description` | string | Issue description |
| ↳ `confidential` | boolean | Whether the issue is confidential |
| ↳ `url` | string | Issue URL |
| ↳ `work_item_type` | string | Work item type \(e.g. Issue, Incident, Task\); GitLab 17.2+ only |


---
Expand Down Expand Up @@ -912,7 +913,8 @@ Trigger workflow when a merge request is opened, updated, or merged in GitLab
| ↳ `action` | string | Action \(open, close, reopen, update, merge, etc.\) |
| ↳ `source_branch` | string | Source branch |
| ↳ `target_branch` | string | Target branch |
| ↳ `merge_status` | string | Merge status |
| ↳ `merge_status` | string | Merge status \(deprecated by GitLab\) |
| ↳ `detailed_merge_status` | string | Detailed merge status |
| ↳ `draft` | boolean | Whether the merge request is a draft |
| ↳ `url` | string | Merge request URL |

Expand Down
2 changes: 1 addition & 1 deletion apps/docs/content/docs/en/integrations/gmail.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ Triggers when new emails are received in Gmail (requires Gmail credentials)
|`date` | string | Email date in ISO format |
|`bodyText` | string | Plain text email body |
|`bodyHtml` | string | HTML email body |
|`labels` | string | Email labels array |
|`labels` | array | Email labels array |
|`hasAttachments` | boolean | Whether email has attachments |
|`attachments` | file[] | Array of email attachments as files \(if includeAttachments is enabled\) |
| `timestamp` | string | Event timestamp |
Expand Down
2 changes: 1 addition & 1 deletion apps/pii/requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Test-only deps. Unit tests need requirements.txt + this file (no models);
# integration tests additionally need the models baked into the docker images
# (see tests/test_integration.py).
pytest==8.4.1
pytest==9.0.3
httpx==0.28.1
4 changes: 2 additions & 2 deletions apps/pii/requirements-gliner.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
# torch is pinned in the Dockerfile instead: the CPU and CUDA targets install
# the same version from different wheel indexes.
gliner==0.2.27
transformers==4.56.2
huggingface_hub==0.35.3
transformers==5.3.0
huggingface_hub==1.3.0
13 changes: 13 additions & 0 deletions apps/sim/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,19 @@ API_ENCRYPTION_KEY=your_api_encryption_key # Use `openssl rand -hex 32` to gener
# S3_ENDPOINT= # Custom endpoint for S3-compatible storage (Cloudflare R2, MinIO, Backblaze B2). Leave unset for AWS S3
# S3_FORCE_PATH_STYLE=true # Required for MinIO/Ceph RGW. Leave unset for AWS S3 and R2

# Azure Blob Storage takes precedence over S3 if both are configured
# AZURE_ACCOUNT_NAME= # Azure storage account name
# AZURE_ACCOUNT_KEY= # Azure storage account key
# AZURE_CONNECTION_STRING= # Alternative to account name/key
# AZURE_STORAGE_CONTAINER_NAME= # General workspace files container
# AZURE_STORAGE_KB_CONTAINER_NAME= # Knowledge base documents
# AZURE_STORAGE_EXECUTION_FILES_CONTAINER_NAME= # Workflow execution files
# AZURE_STORAGE_CHAT_CONTAINER_NAME= # Deployed chat assets
# AZURE_STORAGE_COPILOT_CONTAINER_NAME= # Copilot attachments
# AZURE_STORAGE_PROFILE_PICTURES_CONTAINER_NAME= # User profile pictures
# AZURE_STORAGE_OG_IMAGES_CONTAINER_NAME= # OpenGraph preview images (falls back to AZURE_STORAGE_CONTAINER_NAME)
# AZURE_STORAGE_WORKSPACE_LOGOS_CONTAINER_NAME= # Workspace logos (falls back to AZURE_STORAGE_CONTAINER_NAME)

# Admin API (Optional - for self-hosted GitOps)
# ADMIN_API_KEY= # Use `openssl rand -hex 32` to generate. Enables admin API for workflow export/import.
# Usage: curl -H "x-admin-key: your_key" https://your-instance/api/v1/admin/workspaces
Loading
Loading