Skip to content

Commit 765adba

Browse files
committed
Merge remote-tracking branch 'origin/staging' into worktree-sim-native-kb-connectors
2 parents f3b2559 + 377702f commit 765adba

96 files changed

Lines changed: 2783 additions & 616 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/ship/SKILL.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,8 @@ When the user runs `/ship`:
6969
for s in check:boundaries check:api-validation:strict check:desktop-bridge check:desktop-ipc \
7070
check:utils check:zustand-v5 \
7171
check:react-query check:client-boundary check:bare-icons check:icon-paths \
72-
check:realtime-prune check:tool-registry-boundary tool-metadata:check \
72+
check:realtime-prune check:tool-registry-boundary check:tool-request-boundary \
73+
tool-metadata:check \
7374
integration-catalog:check skills:check agent-stream-docs:check; do
7475
( bun run "$s" >"/tmp/ship-audit-${s//:/-}.log" 2>&1; echo "$? $s" >>/tmp/ship-audit-results ) &
7576
done

.claude/commands/ship.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@ When the user runs `/ship`:
6868
for s in check:boundaries check:api-validation:strict check:desktop-bridge check:desktop-ipc \
6969
check:utils check:zustand-v5 \
7070
check:react-query check:client-boundary check:bare-icons check:icon-paths \
71-
check:realtime-prune check:tool-registry-boundary tool-metadata:check \
71+
check:realtime-prune check:tool-registry-boundary check:tool-request-boundary \
72+
tool-metadata:check \
7273
integration-catalog:check skills:check agent-stream-docs:check; do
7374
( bun run "$s" >"/tmp/ship-audit-${s//:/-}.log" 2>&1; echo "$? $s" >>/tmp/ship-audit-results ) &
7475
done

.cursor/commands/ship.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ When the user runs `/ship`:
6363
for s in check:boundaries check:api-validation:strict check:desktop-bridge check:desktop-ipc \
6464
check:utils check:zustand-v5 \
6565
check:react-query check:client-boundary check:bare-icons check:icon-paths \
66-
check:realtime-prune check:tool-registry-boundary tool-metadata:check \
66+
check:realtime-prune check:tool-registry-boundary check:tool-request-boundary \
67+
tool-metadata:check \
6768
integration-catalog:check skills:check agent-stream-docs:check; do
6869
( bun run "$s" >"/tmp/ship-audit-${s//:/-}.log" 2>&1; echo "$? $s" >>/tmp/ship-audit-results ) &
6970
done

apps/docs/content/docs/en/platform/enterprise/self-hosted.mdx

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,10 @@ ENTERPRISE_ENABLED=true
2323
NEXT_PUBLIC_ENTERPRISE_ENABLED=true
2424
```
2525

26-
That turns on organizations, permission groups, SSO, whitelabeling, audit logs, session policies, data retention, data drains, workspace forks, sandboxes, and the inbox.
26+
That turns on organizations, permission groups, SSO, whitelabeling, audit logs,
27+
session policies, data retention, data drains, workspace forks, the Sandbox
28+
entitlement, and the inbox. Sandboxes remain unavailable until their remote
29+
provider and dedicated Function base are configured.
2730

2831
### Turning one feature off
2932

@@ -95,9 +98,16 @@ NEXT_PUBLIC_SANDBOXES_ENABLED=true
9598
```
9699

97100
`SANDBOXES_ENABLED` grants the server-side self-hosted entitlement.
98-
`NEXT_PUBLIC_SANDBOXES_ENABLED` exposes remote Python and Shell plus custom
99-
sandbox management in the browser. Set the public flag only after the selected
100-
provider has credentials and a valid immutable Function base configured.
101+
`NEXT_PUBLIC_SANDBOXES_ENABLED` projects provider readiness to the browser and
102+
exposes Shell plus custom Sandbox management. Set the public flag only after the
103+
selected provider has credentials and a valid immutable Function base configured.
104+
The Function language value itself is never conditioned on these flags, so a
105+
saved Python block cannot be silently serialized or executed as JavaScript.
106+
107+
JavaScript without `import` or `require` does not use this remote provider and
108+
continues to run in the local isolated VM when all Sandbox flags are off. Python,
109+
Shell, JavaScript with external imports, and selected custom Sandboxes fail with
110+
an explicit configuration error until the remote Function base is ready.
101111

102112
Mothership's `function_execute` and `run_code` tools use Mothership's separate
103113
shell image, including for JavaScript without imports. If the deployment uses

apps/docs/content/docs/en/platform/self-hosting/index.mdx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,12 +119,11 @@ Sim is self-contained for the core editor and execution engine. A few features r
119119
| **Agent blocks** | An API key for at least one model provider | Or a self-hosted OpenAI-compatible endpoint: Ollama, vLLM, or LiteLLM. |
120120
| **Chat module** | `COPILOT_API_KEY` from sim.ai | Set `NEXT_PUBLIC_CHAT_DISABLED=true` to hide the module instead. |
121121
| **Integrations** | Your own OAuth app per service | See [Integrations & OAuth](/platform/self-hosting/integrations-oauth). |
122-
| **Function / Pi blocks at scale** | Optional E2B or Daytona key | Without one, code runs in the in-process isolated-vm sandbox. See [Security](/platform/self-hosting/security). |
122+
| **Remote Function / Pi execution** | Optional E2B or Daytona key | Without one, JavaScript Function code that has no `import` or `require` still runs in the in-process isolated VM. Python, Shell, JavaScript with external imports, custom Function Sandboxes, and Pi require a configured remote provider. See [Security](/platform/self-hosting/security). |
123123

124124
<FAQ items={[
125125
{ question: "What are the minimum requirements to self-host Sim?", answer: "At minimum you need 2 CPU cores, 12 GB RAM, 20 GB SSD storage, and Docker 20.10 or later. Memory is typically the constraining factor due to workflow execution (isolated-vm sandboxing), file processing, and vector operations (pgvector)."},
126126
{ question: "What are the required environment variables for production?", answer: "Three secrets are required: BETTER_AUTH_SECRET (authentication), ENCRYPTION_KEY (data encryption), and INTERNAL_API_SECRET (service-to-service auth). Generate each with openssl rand -hex 32. You also need to set NEXT_PUBLIC_APP_URL and BETTER_AUTH_URL to your domain."},
127127
{ question: "Can I use Sim with local AI models?", answer: "Yes. Sim supports Ollama for local model inference. Use docker-compose.ollama.yml instead of docker-compose.prod.yml. It offers both GPU (with NVIDIA support) and CPU-only profiles, and automatically pulls gemma3:4b as a starter model." },
128128
]} />
129129

130-

apps/docs/content/docs/en/platform/self-hosting/security.mdx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,9 @@ Workflows can execute user-authored JavaScript and Python. Know which sandbox yo
115115
| **E2B** | `E2B_ENABLED=true`, `E2B_API_KEY` | Remote sandbox per execution. Strongest isolation; requires outbound access to E2B. |
116116
| **Daytona** | `SANDBOX_PROVIDER=daytona`, `DAYTONA_API_KEY` | Remote sandbox per execution. |
117117

118-
Python execution and the tooling-dependent blocks require a remote sandbox provider — the in-process isolate runs JavaScript only.
118+
Python, Shell, JavaScript with external imports, and tooling-dependent blocks
119+
require a remote sandbox provider. JavaScript without `import` or `require`
120+
continues to run in the in-process isolate when no remote provider is configured.
119121

120122
<Callout type="warn">
121123
With the default in-process sandbox, treat everyone who can author a workflow as someone running code in your app container's security context. If your Sim instance is open to a wide or partly-trusted audience, use a remote sandbox provider and enable the NetworkPolicy egress restrictions.

apps/docs/content/docs/en/workflows/blocks/function.mdx

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,11 @@ The **Function block** runs your own JavaScript, Python, or Shell code as one st
1616

1717
### Code
1818

19-
JavaScript is the default. Python and Shell appear when a remote sandbox provider is enabled. Reference an earlier output directly, with no quotes around the tag, and read an environment variable with `{{VAR}}`:
19+
JavaScript is the default. The language field is part of the saved workflow and is
20+
never removed when Sandbox configuration changes. Python remains available as a
21+
language choice; Shell and custom Sandbox controls appear when a remote Function
22+
sandbox provider is enabled. Reference an earlier output directly, with no quotes
23+
around the tag, and read an environment variable with `{{VAR}}`:
2024

2125
<Tabs items={['JavaScript', 'Python', 'Shell']}>
2226
<Tab value="JavaScript">
@@ -98,6 +102,13 @@ on a self-hosted instance, build and configure the provider's dedicated
98102
generate are captured as images automatically.
99103
</Callout>
100104

105+
<Callout type="info">
106+
If no remote provider is configured, JavaScript without `import` or `require`
107+
continues to run in Sim's local isolated VM. Missing E2B or Daytona configuration
108+
does not disable that path. Remote-only code fails with an explicit configuration
109+
error; Sim does not reinterpret Python or Shell as JavaScript.
110+
</Callout>
111+
101112
The dedicated Function base has the same runtime and universal package contract
102113
on E2B and Daytona. It includes this data-science stack; use a workspace sandbox
103114
when another dependency must be present:
@@ -128,7 +139,9 @@ Create and edit sandboxes in **Settings → Sandboxes**. Only workspace admins c
128139
create or edit them. On sim.ai they need an active Max or Enterprise plan;
129140
self-hosted deployments turn them on with `SANDBOXES_ENABLED` (see
130141
[self-hosted enterprise](/platform/enterprise/self-hosted)). The section is
131-
hidden when a deployment has no sandbox provider configured.
142+
usable only when the deployment also has a remote provider and immutable Function
143+
base configured. The Function block hides its custom Sandbox selector when that
144+
runtime is unavailable.
132145

133146
1. **Name** the sandbox — `bigquery-etl`, `scraping`, whatever the job is.
134147
2. Pick the **language**. This selects pip or npm for the dependency list. Python
@@ -363,8 +376,9 @@ The lazy `sim.files` and `sim.values` helpers are available only in JavaScript f
363376
- **Use stdout to debug.** `console.log()`, `print()`, and ordinary shell output land in `<function.stdout>` and the run logs.
364377

365378
<FAQ items={[
366-
{ question: "What languages does the Function block support?", answer: "JavaScript, Python, and Shell. JavaScript is the default. Python and Shell appear when a remote sandbox provider is enabled." },
379+
{ question: "What languages does the Function block support?", answer: "JavaScript, Python, and Shell. JavaScript is the default. Python remains a stable saved language choice; Shell and custom Sandbox controls appear when a remote sandbox provider is enabled. Python and Shell execution require that provider." },
367380
{ question: "When does code run locally vs. in a sandbox?", answer: "JavaScript without external imports runs in a local isolated sandbox for speed. JavaScript that uses import or require, Python, and Shell run in the configured remote sandbox." },
381+
{ question: "Does JavaScript still work without E2B or Daytona?", answer: "Yes. JavaScript without import or require runs in Sim's local isolated VM and does not require a remote provider. JavaScript with external imports, Python, Shell, and custom Sandboxes require E2B or Daytona and fail explicitly when it is unavailable." },
368382
{ question: "How do I reference outputs from other blocks inside my code?", answer: "Use angle-bracket syntax directly, like <agent.content> or <api.data>, with no quotes around the tag — Sim replaces it with the real value before execution. For environment variables, use double curly braces: {{API_KEY}}." },
369383
{ question: "What does the Function block return?", answer: "Two outputs: result and stdout. Use return in JavaScript, assign __sim_result__ in Python, or print an __SIM_RESULT__= marker in Shell to set result. Ordinary console, print, and command output goes to stdout." },
370384
{ question: "Can I make HTTP requests from a Function block?", answer: "Yes. fetch() is available in JavaScript with async/await. In Python, use requests or httpx. In Shell, use curl or a CLI available on the selected sandbox." },

apps/sim/app/api/function/execute/route.test.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ describe('Function Execute API Route', () => {
218218
expect(data).toHaveProperty('error', 'Unauthorized')
219219
})
220220

221-
it.concurrent('should use isolated-vm for secure sandboxed execution', async () => {
221+
it('runs import-free JavaScript in isolated-vm without a remote provider', async () => {
222222
const req = createMockRequest('POST', {
223223
code: 'return "test"',
224224
})
@@ -229,6 +229,9 @@ describe('Function Execute API Route', () => {
229229
expect(response.status).toBe(200)
230230
expect(data.success).toBe(true)
231231
expect(data.output.result).toBe('test')
232+
expect(mockExecuteInIsolatedVM).toHaveBeenCalledTimes(1)
233+
expect(mockExecuteInSandbox).not.toHaveBeenCalled()
234+
expect(mockExecuteShellInSandbox).not.toHaveBeenCalled()
232235
})
233236

234237
it('does not accept a Mothership sandbox profile from the request body', async () => {

apps/sim/app/api/guardrails/validate/route.test.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -435,4 +435,31 @@ describe('POST /api/guardrails/validate', () => {
435435
await expect(res.json()).resolves.toEqual({ error: 'Failed to resolve billing attribution' })
436436
expect(mockValidateHallucination).not.toHaveBeenCalled()
437437
})
438+
439+
/**
440+
* The signal now reaches the scoring model, so cancellation is reachable here.
441+
* `passed: false` would read to a consumer as the guardrail rejecting the content,
442+
* blocking a run that was abandoned rather than judged.
443+
*/
444+
it('reports a cancelled run as cancellation rather than a failed guardrail', async () => {
445+
mockAuthorizeCredentialUse.mockResolvedValue({ ok: true })
446+
mockValidateHallucination.mockRejectedValueOnce(
447+
Object.assign(new Error('The operation was aborted.'), { name: 'AbortError' })
448+
)
449+
450+
const res = await POST(
451+
createMockRequest('POST', {
452+
validationType: 'hallucination',
453+
input: 'test input',
454+
knowledgeBaseId: 'kb-1',
455+
model: 'openai/gpt-4o',
456+
workflowId: 'wf-1',
457+
})
458+
)
459+
460+
expect(res.status).toBe(499)
461+
const json = await res.json()
462+
expect(json.success).toBe(false)
463+
expect(json.output?.passed).toBeUndefined()
464+
})
438465
})

apps/sim/app/api/guardrails/validate/route.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import {
2828
ProviderNotAllowedError,
2929
} from '@/ee/access-control/utils/permission-check'
3030
import type { ResolvedSecretTraceRegistry } from '@/executor/utils/resolved-secret-trace-registry'
31+
import { isAbortError } from '@/providers/streaming-tool-loop-shared'
3132
import { getProviderFromModel } from '@/providers/utils'
3233

3334
const logger = createLogger('GuardrailsValidateAPI')
@@ -316,7 +317,8 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
316317
auth.userId,
317318
billingAttribution,
318319
requestId,
319-
resolvedSecretTraceRegistry
320+
resolvedSecretTraceRegistry,
321+
request.signal
320322
)
321323

322324
/**
@@ -371,6 +373,19 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
371373
},
372374
})
373375
} catch (error: any) {
376+
/**
377+
* A cancelled run must not be reshaped into a verdict. `passed: false` here reads
378+
* to a consumer as the guardrail rejecting the content, so an abandoned run would
379+
* block content that was never actually judged. 499 matches the convention the
380+
* workflow execute route already uses for a client-cancelled request.
381+
*/
382+
if (isAbortError(error)) {
383+
logger.info(`[${requestId}] Guardrails validation cancelled by client`)
384+
return NextResponse.json(
385+
{ success: false, error: 'Client cancelled request' },
386+
{ status: 499 }
387+
)
388+
}
374389
logger.error(`[${requestId}] Guardrails validation failed`, { error })
375390
return NextResponse.json({
376391
success: true,
@@ -431,7 +446,8 @@ async function executeValidation(
431446
actorUserId: string,
432447
billingAttribution: BillingAttributionSnapshot | undefined,
433448
requestId: string,
434-
resolvedSecretTraceRegistry: ResolvedSecretTraceRegistry | undefined
449+
resolvedSecretTraceRegistry: ResolvedSecretTraceRegistry | undefined,
450+
abortSignal: AbortSignal | undefined
435451
): Promise<{
436452
passed: boolean
437453
error?: string
@@ -488,6 +504,7 @@ async function executeValidation(
488504
billingAttribution,
489505
requestId,
490506
resolvedSecretTraceRegistry,
507+
abortSignal,
491508
})
492509
}
493510
if (validationType === 'pii') {

0 commit comments

Comments
 (0)