Skip to content

Commit a52fc50

Browse files
authored
Merge pull request #9426 from simstudioai/staging
v0.9.37: workspace files paging and search, token sealing by default, scim hardening, db indexes
2 parents aebe8e9 + 8a14967 commit a52fc50

271 files changed

Lines changed: 202080 additions & 3659 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.

‎.github/scripts/get-ecr-image-digest.sh‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ if [ -n "$ALLOW_MISSING" ] && [ "$ALLOW_MISSING" != '--allow-missing' ]; then
99
exit 1
1010
fi
1111
export AWS_PAGER=''
12-
aws ecr batch-get-image --repository-name "$REPOSITORY" --image-ids imageTag="$TAG" --output json |
12+
aws --cli-connect-timeout 10 --cli-read-timeout 20 ecr batch-get-image --repository-name "$REPOSITORY" --image-ids imageTag="$TAG" --output json |
1313
ALLOW_MISSING="$ALLOW_MISSING" python3 -c '
1414
import json, os, re, sys
1515
response = json.load(sys.stdin)

‎.github/scripts/promote-app-image.sh‎

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,25 @@ RELEASE_TAG="${5:-}"
99
: "${GITHUB_OUTPUT:?GitHub output file required}"
1010
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
1111
PREVIOUS=$(bash "$SCRIPT_DIR/get-ecr-image-digest.sh" "$REPOSITORY" "$DEPLOY_TAG" --allow-missing)
12+
# A tag read straight after the move can still return the previous image, which
13+
# reports a moved tag as unchanged. The dry run prints the exact manifest the
14+
# move pushes (plus a trailing newline), so its hash is the digest the tag will
15+
# point at, known before the move.
16+
MANIFEST=$(docker buildx imagetools create --dry-run -t "$REGISTRY/$REPOSITORY:$DEPLOY_TAG" "$REGISTRY/$REPOSITORY:$SOURCE_TAG")
17+
DIGEST="sha256:$(printf '%s' "$MANIFEST" | sha256sum | cut -d' ' -f1)"
1218
EPOCH=$(date +%s)
1319
bash "$SCRIPT_DIR/promote-image.sh" "$REGISTRY" "$REPOSITORY" "$SOURCE_TAG" "$DEPLOY_TAG" "$RELEASE_TAG"
14-
DIGEST=$(bash "$SCRIPT_DIR/get-ecr-image-digest.sh" "$REPOSITORY" "$DEPLOY_TAG")
20+
deadline=$(( $(date +%s) + 60 ))
21+
while true; do
22+
# Assigned, not tested in the loop condition, so a failed lookup stops the script under set -e.
23+
CURRENT=$(bash "$SCRIPT_DIR/get-ecr-image-digest.sh" "$REPOSITORY" "$DEPLOY_TAG" --allow-missing)
24+
[ "$CURRENT" = "$DIGEST" ] && break
25+
if [ "$(date +%s)" -ge "$deadline" ]; then
26+
echo "ERROR: $DEPLOY_TAG did not resolve to the pushed $DIGEST within 60s; cutover cannot be correlated" >&2
27+
exit 1
28+
fi
29+
sleep 3
30+
done
1531
CHANGED=true
1632
if [ "$DIGEST" = "$PREVIOUS" ]; then CHANGED=false; fi
1733
{

‎.github/workflows/ci.yml‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -273,21 +273,21 @@ jobs:
273273
id: deploy
274274
working-directory: ./apps/sim
275275
env:
276-
TRIGGER_ACCESS_TOKEN: ${{ secrets.TRIGGER_ACCESS_TOKEN }}
276+
TRIGGER_ACCESS_TOKEN: ${{ (steps.target.outputs.environment == 'prod' && secrets.TRIGGER_ACCESS_TOKEN_PROD) || (steps.target.outputs.environment == 'staging' && secrets.TRIGGER_ACCESS_TOKEN_STAGING) || secrets.TRIGGER_ACCESS_TOKEN }}
277277
TRIGGER_PROJECT_ID: ${{ secrets.TRIGGER_PROJECT_ID }}
278278
TRIGGER_ENV: ${{ steps.target.outputs.environment }}
279279
TRIGGER_BRANCH: ${{ steps.target.outputs.preview_branch }}
280280
run: |
281281
set -eo pipefail
282282
if [ -z "$TRIGGER_ACCESS_TOKEN" ] || [ -z "$TRIGGER_PROJECT_ID" ]; then
283-
echo "ERROR: TRIGGER_ACCESS_TOKEN and TRIGGER_PROJECT_ID repo secrets must both be set" >&2
283+
echo "ERROR: TRIGGER_PROJECT_ID and a Trigger.dev access token (TRIGGER_ACCESS_TOKEN_${TRIGGER_ENV^^} or TRIGGER_ACCESS_TOKEN) repo secrets must be set" >&2
284284
exit 1
285285
fi
286286
TARGET_ARGS=(--env "$TRIGGER_ENV")
287287
if [ -n "$TRIGGER_BRANCH" ]; then
288288
TARGET_ARGS+=(--branch "$TRIGGER_BRANCH")
289289
fi
290-
bunx trigger.dev@4.5.16 deploy "${TARGET_ARGS[@]}" --skip-promotion
290+
bunx trigger.dev@4.7.6 deploy "${TARGET_ARGS[@]}" --skip-promotion
291291
292292
- name: Validate deployment version output
293293
env:
@@ -618,15 +618,15 @@ jobs:
618618
- name: Promote Trigger.dev version
619619
working-directory: ./apps/sim
620620
env:
621-
TRIGGER_ACCESS_TOKEN: ${{ secrets.TRIGGER_ACCESS_TOKEN }}
621+
TRIGGER_ACCESS_TOKEN: ${{ (needs.trigger-upload.outputs.environment == 'prod' && secrets.TRIGGER_ACCESS_TOKEN_PROD) || (needs.trigger-upload.outputs.environment == 'staging' && secrets.TRIGGER_ACCESS_TOKEN_STAGING) || secrets.TRIGGER_ACCESS_TOKEN }}
622622
TRIGGER_PROJECT_ID: ${{ secrets.TRIGGER_PROJECT_ID }}
623623
TRIGGER_ENV: ${{ needs.trigger-upload.outputs.environment }}
624624
TRIGGER_BRANCH: ${{ needs.trigger-upload.outputs.preview_branch }}
625625
VERSION: ${{ needs.trigger-upload.outputs.version }}
626626
run: |
627627
set -eo pipefail
628628
if [ -z "$TRIGGER_ACCESS_TOKEN" ] || [ -z "$TRIGGER_PROJECT_ID" ]; then
629-
echo "ERROR: TRIGGER_ACCESS_TOKEN and TRIGGER_PROJECT_ID repo secrets must both be set" >&2
629+
echo "ERROR: TRIGGER_PROJECT_ID and a Trigger.dev access token (TRIGGER_ACCESS_TOKEN_${TRIGGER_ENV^^} or TRIGGER_ACCESS_TOKEN) repo secrets must be set" >&2
630630
exit 1
631631
fi
632632
if [ -z "$VERSION" ]; then
@@ -638,7 +638,7 @@ jobs:
638638
if [ -n "$TRIGGER_BRANCH" ]; then
639639
TARGET_ARGS+=(--branch "$TRIGGER_BRANCH")
640640
fi
641-
bunx trigger.dev@4.5.16 promote "$VERSION" "${TARGET_ARGS[@]}"
641+
bunx trigger.dev@4.7.6 promote "$VERSION" "${TARGET_ARGS[@]}"
642642
643643
# Production only, once the new app serves traffic: submit the library and comparison URLs this
644644
# push changed to IndexNow. Best effort: nothing depends on it, and a failure never fails the run.

‎.github/workflows/desktop-e2e.yml‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,9 +168,11 @@ jobs:
168168
- name: Install dependencies
169169
run: bun install --frozen-lockfile
170170

171+
# The bunfig age gate would resolve `latest` to the newest week-old release; the canary
172+
# exists to test the release that just shipped. Already-locked packages stay locked.
171173
- name: Switch to electron@latest
172174
working-directory: apps/desktop
173-
run: bun add --no-save -d electron@latest
175+
run: bun add --no-save -d --minimum-release-age=0 electron@latest
174176

175177
- name: Bundle main and preload
176178
working-directory: apps/desktop

‎.github/workflows/trigger-dev.yml‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,19 +35,19 @@ jobs:
3535
id: deploy
3636
working-directory: ./apps/sim
3737
env:
38-
TRIGGER_ACCESS_TOKEN: ${{ secrets.TRIGGER_ACCESS_TOKEN }}
38+
TRIGGER_ACCESS_TOKEN: ${{ secrets.TRIGGER_ACCESS_TOKEN_PREVIEW || secrets.TRIGGER_ACCESS_TOKEN }}
3939
TRIGGER_PROJECT_ID: ${{ secrets.TRIGGER_PROJECT_ID }}
4040
run: |
4141
set -euo pipefail
42-
: "${TRIGGER_ACCESS_TOKEN:?TRIGGER_ACCESS_TOKEN must be configured}"
42+
: "${TRIGGER_ACCESS_TOKEN:?TRIGGER_ACCESS_TOKEN_PREVIEW or TRIGGER_ACCESS_TOKEN must be configured}"
4343
: "${TRIGGER_PROJECT_ID:?TRIGGER_PROJECT_ID must be configured}"
44-
bunx trigger.dev@4.5.16 deploy --env preview --branch dev-sim --skip-promotion
44+
bunx trigger.dev@4.7.6 deploy --env preview --branch dev-sim --skip-promotion
4545
4646
- name: Promote current dev preview
4747
working-directory: ./apps/sim
4848
env:
4949
GH_TOKEN: ${{ github.token }}
50-
TRIGGER_ACCESS_TOKEN: ${{ secrets.TRIGGER_ACCESS_TOKEN }}
50+
TRIGGER_ACCESS_TOKEN: ${{ secrets.TRIGGER_ACCESS_TOKEN_PREVIEW || secrets.TRIGGER_ACCESS_TOKEN }}
5151
TRIGGER_PROJECT_ID: ${{ secrets.TRIGGER_PROJECT_ID }}
5252
VERSION: ${{ steps.deploy.outputs.deploymentVersion }}
5353
run: |
@@ -61,4 +61,4 @@ jobs:
6161
echo "::notice::Skipping superseded dev task version $VERSION"
6262
exit 0
6363
fi
64-
bunx trigger.dev@4.5.16 promote "$VERSION" --env preview --branch dev-sim
64+
bunx trigger.dev@4.7.6 promote "$VERSION" --env preview --branch dev-sim

‎apps/desktop/e2e/source-connect.spec.ts‎

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ test('source authorization returns to its desktop screen and refreshes live', as
5353
const githubStartSessions: string[] = []
5454
const githubInventorySessions: string[] = []
5555
let nativeCredentialVisible = false
56+
let githubInventoryHold: Promise<void> | undefined
5657
let installed = false
5758
let holdSlackStart = false
5859
let canceledSlackRequests = 0
@@ -304,7 +305,7 @@ test('source authorization returns to its desktop screen and refreshes live', as
304305
}
305306
if (path === '/api/organization-credentials/oauth') {
306307
githubInventorySessions.push(session)
307-
await sleep(500)
308+
await githubInventoryHold
308309
json({
309310
credentials: nativeCredentialVisible
310311
? [
@@ -545,12 +546,21 @@ test('source authorization returns to its desktop screen and refreshes live', as
545546
await expect(page.getByLabel('GitHub error')).toContainText('not available')
546547
await expect(page.getByLabel('GitHub credential')).toHaveText('')
547548
nativeCredentialVisible = true
548-
await page.getByRole('button', { name: 'Connect GitHub' }).click()
549-
await expect.poll(async () => (await opened()).length).toBe(6)
550-
await external.goto((await opened())[5])
551-
await external.getByRole('link', { name: 'Authorize GitHub' }).click()
552-
await expect.poll(() => githubInventorySessions.length).toBe(2)
553-
await expect(page.getByLabel('GitHub pending')).toHaveText('true')
549+
let releaseGithubInventory = () => {}
550+
githubInventoryHold = new Promise<void>((resolve) => {
551+
releaseGithubInventory = resolve
552+
})
553+
try {
554+
await page.getByRole('button', { name: 'Connect GitHub' }).click()
555+
await expect.poll(async () => (await opened()).length).toBe(6)
556+
await external.goto((await opened())[5])
557+
await external.getByRole('link', { name: 'Authorize GitHub' }).click()
558+
await expect.poll(() => githubInventorySessions.length).toBe(2)
559+
await expect(page.getByLabel('GitHub pending')).toHaveText('true')
560+
} finally {
561+
githubInventoryHold = undefined
562+
releaseGithubInventory()
563+
}
554564
await expect(page.getByLabel('GitHub credential')).toHaveText('fixture-github-credential')
555565
expect(githubStartSessions).toEqual(['browser-fixture', 'browser-fixture'])
556566
expect(githubInventorySessions).toEqual(['desktop-fixture', 'desktop-fixture'])

‎apps/docs/content/docs/cli/index.mdx‎

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,39 @@ sim tables rows query --help
142142

143143
The [command overview](/cli/commands) lists global options and root commands. The [complete reference](/cli/reference) contains every subcommand, argument, and option on one page. Both are generated from the CLI.
144144

145+
## Agent skills
146+
147+
The [Sim skills](https://github.com/simstudioai/skills) teach coding agents such as Claude Code and Codex to build and operate Sim through the CLI. Install them with the standard Agent Skills installer:
148+
149+
```bash
150+
bunx skills add simstudioai/skills --global
151+
```
152+
153+
To install one skill for one agent without prompting:
154+
155+
```bash
156+
bunx skills add simstudioai/skills \
157+
--skill build-workflow \
158+
--agent codex \
159+
--global \
160+
--yes
161+
```
162+
163+
| Skill | What it covers |
164+
| --- | --- |
165+
| `build-workflow` | Discover blocks and author a draft graph with atomic workflow operations |
166+
| `run-workflow` | Test saved state, exercise triggers, resume from a block, and diagnose runs |
167+
| `deploy-workflow` | Publish and manage workflows as APIs, chats, or MCP tools |
168+
| `files` | Organize workspace files and folders, read and search text, and apply targeted edits |
169+
| `sync-workspaces` | Import portable workflows with bindings, fork workspaces, and preview, apply, and reconcile push/pull sync |
170+
| `table` | Design typed tables, load and query rows, import data, and run workflow groups |
171+
| `knowledge-base` | Ingest and index documents, configure connectors and tags, and verify retrieval |
172+
| `run-tool` | Call one integration tool directly with Sim-managed credentials |
173+
174+
The repository is also a native Claude Code and Codex plugin, which exposes the same skills under the `sim` namespace (`sim:build-workflow`, `sim:run-workflow`, and so on).
175+
176+
The skills assume `sim` is installed and signed in, so complete [Your first command](#your-first-command) first. They never store or print API keys. These agent skills are separate from the workspace skills you manage with [`sim skills`](/cli/skills).
177+
145178
## Related docs
146179

147180
- [Authentication](/cli/authentication)

‎apps/docs/content/docs/platform/enterprise/scim/entra.mdx‎

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ Use a non-gallery enterprise application in Microsoft Entra ID to create, update
1111

1212
## Before you start
1313

14-
- Use an Enterprise organization in Sim and an organization owner or admin account.
14+
- On Sim Cloud, use an Enterprise organization. Use an organization owner or admin account to configure provisioning.
1515
- [Verify each provisioned email domain](/platform/enterprise/verified-domains).
1616
- Use an Entra administrator who can create enterprise applications and manage provisioning.
1717
- Make Sim reachable by Entra over HTTPS. Self-hosted deployments must meet the [SCIM rollout requirements](/platform/enterprise/scim#deployment-and-upgrades).
@@ -20,6 +20,8 @@ Automated user provisioning is available with Entra ID Free. Group provisioning
2020

2121
Start with dedicated test accounts. Provisioned members use a seat and receive workspace access through mappings or existing grants.
2222

23+
An existing SAML or OIDC sign-in setup can remain in place. Use the same account email for authentication and provisioning, and review app assignments if they use separate enterprise applications. See [Plan membership and access](/platform/enterprise/scim#plan-membership-and-access) for the distinction between sign-in, organization membership, workspace access, and permission policy.
24+
2325
## Connect Entra to Sim
2426

2527
<Steps>
@@ -101,12 +103,13 @@ Sim deactivation suspends access while retaining membership, ownership, and the
101103

102104
## Provision groups and verify access
103105

104-
With P1 or higher, create a security group with **Assigned** membership and add test accounts as direct members. Assign it under **Users and groups**. Review **Attribute mapping** and keep group provisioning enabled.
106+
With P1 or higher, create a security group with **Assigned** membership and add test accounts as direct members. Assign it under **Users and groups**. Review **Attribute mapping** and keep group provisioning enabled. Create the target workspace and a dedicated permission group in Sim, configuring that policy's restrictions and workspace scope before mapping it.
105107

106108
1. Run **Provision on demand** for one group and select up to five test members, or wait for a scheduled cycle. Nested membership is unsupported; see [Microsoft group assignment](https://learn.microsoft.com/en-us/entra/identity/enterprise-apps/assign-user-or-group-access-portal).
107-
2. Once the group appears under **Single sign-on → Provisioning → Group mappings** in Sim, map it to a workspace, permission group, or organization admin role. Start with a workspace mapping and confirm access.
108-
3. Remove a test member from the group while retaining a separate direct app assignment. After synchronization, confirm the directory workspace grant is withdrawn while the account remains active.
109-
4. Add the member back and verify access returns.
109+
2. Once the group appears under **Single sign-on → Provisioning → Group mappings** in Sim, add a **Workspace** mapping and a **Permission group** mapping to the policy created above. Confirm both workspace access and policy restrictions. A permission-group mapping alone does not grant workspace access; an organization-admin mapping is a separate privilege.
110+
3. For automatic policy matching, enable **Provisioning rules → Manage rules → Match permission groups by name**. Names must match an existing non-default Sim permission group exactly, including case. Select **Reconcile now** for groups already synced. Sim does not create the permission group.
111+
4. Remove a test member from the group while retaining a separate direct app assignment. After synchronization, confirm the directory workspace grant is withdrawn while the account remains active. Check the [fallback policy](/platform/enterprise/scim#how-access-is-withdrawn) on any workspace the person can still access.
112+
5. Add the member back and verify access and restrictions return.
110113

111114
Read [How access is withdrawn](/platform/enterprise/scim#how-access-is-withdrawn) before mapping groups that overlap manual access.
112115

@@ -133,6 +136,8 @@ Issue a replacement token in Sim while the current one remains active. In Entra'
133136
| Disablement or deletion has not reached Sim | Inspect the scheduled log, `IsSoftDeleted` mapping to `active`, enabled **Update** actions, and accidental deletion protection. Confirm **Skip out of scope deletions** is disabled. A connection test does not verify lifecycle changes. |
134137
| Group assignment is unavailable | Check licensing. Free supports individual assignment; group assignment and provisioning require an upgrade. |
135138
| Member has no workspace access | Provision the directory group and add a Sim workspace mapping. User provisioning adds organization membership. |
139+
| Member has access but the expected policy does not apply | Check **Group mappings** for unassigned members. Resolve overlapping membership or workspace scope under **Permission groups**, then select **Reconcile now**. For name matching, also check the exact group name. |
140+
| A removed mapping returns | Turn off name matching or use different names before removing a same-name mapping. See [mapping behavior](/platform/enterprise/scim#map-directory-groups). |
136141
| A disabled member still uses a seat | Deactivation suspends access; organization removal is separate. |
137142

138143
Use Entra's **Provisioning logs** and Sim's **Provisioning → Activity**. Invalid or revoked tokens appear only in Entra because Sim cannot associate them with a connection.

0 commit comments

Comments
 (0)