Skip to content

Commit 11168f9

Browse files
authored
v0.7.14: perf improvements, code hygiene, GitLab private host support
2 parents ad0b867 + 34d32b9 commit 11168f9

160 files changed

Lines changed: 8625 additions & 860 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.

.claude/rules/sim-architecture.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ packages/ # @sim/* — audit, auth, db, logger, realtime-protocol
3838
- `apps/* → packages/*` only. Packages never import from `apps/*`.
3939
- `apps/realtime` avoids Next.js, React, the block/tool registry, provider SDKs, and the executor; never add `@/lib/webhooks/providers/*`, `@/executor/*`, `@/blocks/*`, or `@/tools/*` imports to any package it consumes. CI enforces this via `scripts/check-monorepo-boundaries.ts` and `scripts/check-realtime-prune-graph.ts`.
4040

41+
## The `'use client'` server boundary
42+
43+
Every export of a `'use client'` module becomes a *client reference* on the server — server-evaluated code (RSC pages/layouts, `prefetch.ts`, route handlers, block definitions, triggers) can only *render* it as a component or pass it as a prop, never *call* it (doing so throws at runtime, e.g. `tableKeys.list is not a function`; `next build` does not catch it). Keep server-importable query primitives (key factories, fetchers, mappers, constants) in non-`'use client'` modules — see `.claude/rules/sim-queries.md`. Enforced by `scripts/check-client-boundary-imports.ts`.
44+
4145
## Feature Organization
4246

4347
Features live under `app/workspace/[workspaceId]/`:

.claude/rules/sim-queries.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,17 @@ Never use inline query keys — always use the factory.
2727

2828
**Every identifier the `queryFn` forwards into the fetch MUST appear in the `queryKey`.** (Query-machinery identifiers — `signal`, `pageParam` — are exempt; they aren't fetch-scoping args.) If the fetch is scoped by `workspaceId`, `cursor`, `limit`, an org id, etc., those values must be part of the key — otherwise distinct fetch args share one cache entry (a cross-tenant / per-param cache collision). The lone exception is a globally-unique id used as the key while a second fetch arg is only an authz scope that cannot collide; annotate those with `// rq-lint-allow: <reason>`. Enforced by the `key-fetch-arg-drift` check in `scripts/check-react-query-patterns.ts`.
2929

30+
## Server-importable query primitives must NOT live in a `'use client'` module
31+
32+
Next.js rewrites **every** export of a `'use client'` module into a *client reference* in the server bundle. Server-evaluated code — RSC `page.tsx`/`layout.tsx`, `prefetch.ts`, route handlers, **block definitions**, triggers/workers — can only *render* such an export as a component or pass it as a prop; **calling** one throws at runtime (`Attempted to call X from the server but X is on the client` — for an object export it surfaces as `X.list is not a function`). `next build` does **not** catch this — only SSR/runtime does.
33+
34+
So any **query-key factory, standalone `requestJson` fetcher, mapper, or constant** that a server module imports must live in a **non-`'use client'`** module:
35+
36+
- key factories → `hooks/queries/utils/<entity>-keys.ts` (see `folder-keys.ts`, `table-keys.ts`, `credential-keys.ts`)
37+
- standalone fetchers/mappers → `hooks/queries/utils/fetch-*.ts` / `*-list-query.ts` (see `fetch-workflow-envelope.ts`, `fetch-credential-set.ts`)
38+
39+
The `'use client'` hook module then imports these back for its hooks. **Never** define a server-imported factory/fetcher directly in a `'use client'` hooks file — it crashes SSR (this caused the tables-page crash). Enforced for prefetch/route/trigger/block files by `scripts/check-client-boundary-imports.ts` (`bun run check:client-boundary`, run in CI). Escape hatch for a genuinely browser-only path: `// client-boundary-allow: <reason>` on the line above the import.
40+
3041
## File Structure
3142

3243
```typescript

.github/workflows/test-build.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,9 @@ jobs:
122122
- name: React Query pattern audit
123123
run: bun run check:react-query
124124

125+
- name: Client boundary import audit
126+
run: bun run check:client-boundary
127+
125128
- name: Verify realtime prune graph
126129
run: bun run check:realtime-prune
127130

apps/docs/content/docs/en/integrations/gitlab.mdx

Lines changed: 464 additions & 0 deletions
Large diffs are not rendered by default.

apps/docs/content/docs/en/integrations/salesforce.mdx

Lines changed: 147 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ The Salesforce tool is ideal for workflows where your agents need to streamline
2727

2828
## Usage Instructions
2929

30-
Integrate Salesforce into your workflow. Manage accounts, contacts, leads, opportunities, cases, and tasks with powerful automation capabilities.
30+
Integrate Salesforce into your workflow. Manage accounts, contacts, leads, opportunities, cases, and tasks, run reports and SOQL queries, and manage org schema by creating custom fields and objects via the Tooling API.
3131

3232

3333

@@ -717,7 +717,7 @@ Delete a task
717717

718718
### `salesforce_list_reports`
719719

720-
Get a list of reports accessible by the current user
720+
Get a list of up to 200 recently viewed reports for the current user
721721

722722
#### Input
723723

@@ -814,7 +814,7 @@ Get a list of available report types
814814

815815
### `salesforce_list_dashboards`
816816

817-
Get a list of dashboards accessible by the current user
817+
Get a list of recently used dashboards for the current user
818818

819819
#### Input
820820

@@ -1029,6 +1029,150 @@ Get a list of all available Salesforce objects
10291029
|`totalReturned` | number | Number of objects returned |
10301030
|`success` | boolean | Salesforce operation success |
10311031

1032+
### `salesforce_create_custom_field`
1033+
1034+
Create a custom field on a Salesforce object (e.g., Account) using the Tooling API
1035+
1036+
#### Input
1037+
1038+
| Parameter | Type | Required | Description |
1039+
| --------- | ---- | -------- | ----------- |
1040+
| `idToken` | string | No | No description |
1041+
| `instanceUrl` | string | No | No description |
1042+
| `objectName` | string | Yes | API name of the object to add the field to \(e.g., Account, Contact, Lead, MyObject__c\) |
1043+
| `fieldName` | string | Yes | API name of the new field; the __c suffix is added automatically \(e.g., Region\) |
1044+
| `label` | string | No | Display label shown in the UI \(defaults to the field name when omitted\) |
1045+
| `fieldType` | string | Yes | Field data type: Text, TextArea, LongTextArea, Html, Number, Currency, Percent, Checkbox, Date, DateTime, Time, Phone, Email, Url, Picklist, or MultiselectPicklist |
1046+
| `length` | number | No | Maximum length for Text \(1-255\), LongTextArea, Html, or MultiselectPicklist fields |
1047+
| `precision` | number | No | Total number of digits for Number, Currency, or Percent fields \(1-18\) |
1048+
| `scale` | number | No | Number of digits to the right of the decimal for numeric fields |
1049+
| `visibleLines` | number | No | Number of visible lines for LongTextArea, Html, or MultiselectPicklist fields |
1050+
| `required` | boolean | No | Whether the field is required on record create/edit |
1051+
| `unique` | boolean | No | Whether the field enforces unique values |
1052+
| `externalId` | boolean | No | Whether the field is an external ID \(for Text, Number, or Email fields\) |
1053+
| `defaultValue` | string | No | Default value; for Checkbox fields use true or false |
1054+
| `description` | string | No | Internal description of the field |
1055+
| `inlineHelpText` | string | No | Help text shown next to the field in the UI |
1056+
| `picklistValues` | string | No | Comma-separated values for Picklist or MultiselectPicklist fields |
1057+
1058+
#### Output
1059+
1060+
| Parameter | Type | Description |
1061+
| --------- | ---- | ----------- |
1062+
| `success` | boolean | Operation success status |
1063+
| `output` | object | Created custom field metadata |
1064+
|`id` | string | Tooling API Id of the newly created custom field |
1065+
|`fullName` | string | Full API name of the field, including object \(e.g., Account.Region__c\) |
1066+
|`success` | boolean | Whether the create operation was successful |
1067+
|`created` | boolean | Whether the field was created \(always true on success\) |
1068+
1069+
### `salesforce_update_custom_field`
1070+
1071+
Update an existing custom field on a Salesforce object using the Tooling API
1072+
1073+
#### Input
1074+
1075+
| Parameter | Type | Required | Description |
1076+
| --------- | ---- | -------- | ----------- |
1077+
| `idToken` | string | No | No description |
1078+
| `instanceUrl` | string | No | No description |
1079+
| `fieldId` | string | Yes | Tooling API Id of the custom field to update \(find it via the Tooling Query tool\) |
1080+
| `label` | string | No | Display label shown in the UI |
1081+
| `length` | number | No | Maximum length for Text, LongTextArea, Html, or MultiselectPicklist fields |
1082+
| `precision` | number | No | Total number of digits for Number, Currency, or Percent fields |
1083+
| `scale` | number | No | Number of digits to the right of the decimal for numeric fields |
1084+
| `visibleLines` | number | No | Number of visible lines for LongTextArea, Html, or MultiselectPicklist fields |
1085+
| `required` | boolean | No | Whether the field is required on record create/edit |
1086+
| `unique` | boolean | No | Whether the field enforces unique values |
1087+
| `externalId` | boolean | No | Whether the field is an external ID |
1088+
| `defaultValue` | string | No | Default value; for Checkbox fields use true or false |
1089+
| `description` | string | No | Internal description of the field |
1090+
| `inlineHelpText` | string | No | Help text shown next to the field in the UI |
1091+
| `picklistValues` | string | No | Comma-separated values to add to a Picklist or MultiselectPicklist field \(existing values are kept\) |
1092+
1093+
#### Output
1094+
1095+
| Parameter | Type | Description |
1096+
| --------- | ---- | ----------- |
1097+
| `success` | boolean | Operation success status |
1098+
| `output` | object | Updated custom field metadata |
1099+
|`id` | string | Tooling API Id of the updated custom field |
1100+
|`updated` | boolean | Whether the field was updated \(always true on success\) |
1101+
1102+
### `salesforce_delete_custom_field`
1103+
1104+
Delete a custom field from a Salesforce object using the Tooling API
1105+
1106+
#### Input
1107+
1108+
| Parameter | Type | Required | Description |
1109+
| --------- | ---- | -------- | ----------- |
1110+
| `idToken` | string | No | No description |
1111+
| `instanceUrl` | string | No | No description |
1112+
| `fieldId` | string | Yes | Tooling API Id of the custom field to delete \(find it via the Tooling Query tool\) |
1113+
1114+
#### Output
1115+
1116+
| Parameter | Type | Description |
1117+
| --------- | ---- | ----------- |
1118+
| `success` | boolean | Operation success status |
1119+
| `output` | object | Deleted custom field metadata |
1120+
|`id` | string | Tooling API Id of the deleted custom field |
1121+
|`deleted` | boolean | Whether the field was deleted \(always true on success\) |
1122+
1123+
### `salesforce_create_custom_object`
1124+
1125+
Create a custom object in Salesforce using the Tooling API
1126+
1127+
#### Input
1128+
1129+
| Parameter | Type | Required | Description |
1130+
| --------- | ---- | -------- | ----------- |
1131+
| `idToken` | string | No | No description |
1132+
| `instanceUrl` | string | No | No description |
1133+
| `objectName` | string | Yes | API name of the new object; the __c suffix is added automatically \(e.g., Project\) |
1134+
| `label` | string | Yes | Singular display label for the object \(e.g., Project\) |
1135+
| `pluralLabel` | string | Yes | Plural display label for the object \(e.g., Projects\) |
1136+
| `nameFieldLabel` | string | No | Label for the standard Name field \(defaults to "&lt;label&gt; Name"\) |
1137+
| `description` | string | No | Internal description of the object |
1138+
| `sharingModel` | string | No | Org-wide sharing model: ReadWrite, Read, Private, or ControlledByParent \(default ReadWrite\) |
1139+
1140+
#### Output
1141+
1142+
| Parameter | Type | Description |
1143+
| --------- | ---- | ----------- |
1144+
| `success` | boolean | Operation success status |
1145+
| `output` | object | Created custom object metadata |
1146+
|`id` | string | Tooling API Id of the newly created custom object |
1147+
|`fullName` | string | Full API name of the object \(e.g., Project__c\) |
1148+
|`success` | boolean | Whether the create operation was successful |
1149+
|`created` | boolean | Whether the object was created \(always true on success\) |
1150+
1151+
### `salesforce_tooling_query`
1152+
1153+
Execute a SOQL query against the Tooling API to inspect metadata objects
1154+
1155+
#### Input
1156+
1157+
| Parameter | Type | Required | Description |
1158+
| --------- | ---- | -------- | ----------- |
1159+
| `idToken` | string | No | No description |
1160+
| `instanceUrl` | string | No | No description |
1161+
| `query` | string | Yes | Tooling SOQL query \(e.g., SELECT Id, DeveloperName FROM CustomField WHERE TableEnumOrId = 'Account'\) |
1162+
1163+
#### Output
1164+
1165+
| Parameter | Type | Description |
1166+
| --------- | ---- | ----------- |
1167+
| `success` | boolean | Operation success status |
1168+
| `output` | object | Tooling query results |
1169+
|`records` | array | Array of Tooling API records matching the query |
1170+
|`query` | string | The executed Tooling SOQL query |
1171+
|`metadata` | object | Response metadata |
1172+
|`totalReturned` | number | Number of records returned in this response |
1173+
|`hasMore` | boolean | Whether more records exist \(inverse of done\) |
1174+
|`success` | boolean | Salesforce operation success |
1175+
10321176

10331177

10341178
## Triggers

apps/docs/content/docs/en/workflows/blocks/meta.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
"title": "Core Blocks",
33
"pages": [
44
"agent",
5-
"pi",
65
"api",
76
"function",
87
"condition",
@@ -17,6 +16,7 @@
1716
"human-in-the-loop",
1817
"variables",
1918
"wait",
20-
"credential"
19+
"credential",
20+
"pi"
2121
]
2222
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ Pick the mode with the **Mode** dropdown. The fields below it change to match.
2525
Cloud runs entirely inside a disposable sandbox, so it never touches your machine. It clones the repo, lets the agent work with full read/shell/edit/git, pushes a branch, and opens a PR you review and merge.
2626

2727
- Requires sandbox execution to be enabled (the Cloud option only appears when it is).
28-
- Requires **your own provider API key (BYOK)** — the model key is handed to the sandbox, so Sim never injects a hosted key there.
28+
- Requires **your own provider API key (BYOK)** — the model key is handed to the sandbox.
2929
- Needs a **GitHub token** with permission to clone, push, and open a PR (see [Setup](#setup-cloud)).
3030
- The deliverable is a **pull request** — nothing is committed to your default branch directly.
3131

@@ -118,7 +118,7 @@ The one case neither layer can rescue is a *first* prompt that already exceeds t
118118

119119
## Setup
120120

121-
### Cloud
121+
### Cloud [#setup-cloud]
122122

123123
Cloud runs in a sandbox image with the Pi CLI and git baked in.
124124

@@ -128,7 +128,7 @@ Cloud runs in a sandbox image with the Pi CLI and git baked in.
128128
- *Fine-grained:* select the repo, then **Contents: Read and write** + **Pull requests: Read and write**.
129129
- *Classic:* the **`repo`** scope. For org repos, authorize the token for SSO.
130130

131-
### Local
131+
### Local [#setup-local]
132132

133133
1. **Enable SSH** on the target machine (on macOS: System Settings → General → Sharing → Remote Login).
134134
2. **Expose it on a public host.** Sim blocks `localhost`/LAN, so use a TCP tunnel — for example `ngrok tcp 22`, which gives a `host:port` to put in **Host** and **Port**.

apps/realtime/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
"@sim/logger": "workspace:*",
2727
"@sim/platform-authz": "workspace:*",
2828
"@sim/realtime-protocol": "workspace:*",
29+
"@sim/runtime-secrets": "workspace:*",
2930
"@sim/security": "workspace:*",
3031
"@sim/utils": "workspace:*",
3132
"@sim/workflow-persistence": "workspace:*",

apps/realtime/src/bootstrap.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/**
2+
* Container entrypoint. Hydrates `process.env` from the runtime secret before
3+
* loading the Socket.IO server, whose modules (`@/env`, DB preflight) read env
4+
* at import time. See `@sim/runtime-secrets`.
5+
*/
6+
import { loadRuntimeSecrets } from '@sim/runtime-secrets'
7+
8+
await loadRuntimeSecrets()
9+
await import('@/index')

apps/sim/app/api/files/export/[id]/route.ts

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { NextResponse } from 'next/server'
77
import { fileExportContract } from '@/lib/api/contracts/storage-transfer'
88
import { parseRequest } from '@/lib/api/server'
99
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
10+
import { extractEmbeddedImageIds } from '@/lib/copilot/tools/server/files/embedded-image-refs'
1011
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
1112
import type { StorageContext } from '@/lib/uploads/config'
1213
import { USE_BLOB_STORAGE } from '@/lib/uploads/config'
@@ -19,9 +20,6 @@ const logger = createLogger('FilesExportAPI')
1920

2021
const MARKDOWN_MIME_TYPES = new Set(['text/markdown', 'text/x-markdown'])
2122
const MARKDOWN_EXTENSIONS = new Set(['md', 'markdown'])
22-
const VIEW_URL_RE =
23-
/\/api\/files\/view\/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/gi
24-
const MAX_EMBEDDED_IMAGES = 50
2523

2624
function isMarkdown(originalName: string, contentType: string): boolean {
2725
if (MARKDOWN_MIME_TYPES.has(contentType)) return true
@@ -82,10 +80,7 @@ export const GET = withRouteHandler(
8280
})
8381
let mdContent = mdBuffer.toString('utf-8')
8482

85-
const imageIds = [...new Set([...mdContent.matchAll(VIEW_URL_RE)].map((m) => m[1]))].slice(
86-
0,
87-
MAX_EMBEDDED_IMAGES
88-
)
83+
const imageIds = extractEmbeddedImageIds(mdContent)
8984

9085
logger.info('Exporting markdown', { id, imageCount: imageIds.length })
9186

@@ -139,10 +134,11 @@ export const GET = withRouteHandler(
139134
for (const [imageId, asset] of assetMap) {
140135
const escapedId = imageId.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
141136
const replacement = `./assets/${asset.filename}`
142-
mdContent = mdContent.replace(
143-
new RegExp(`/api/files/view/${escapedId}`, 'g'),
144-
() => replacement
145-
)
137+
// Rewrite both embed spellings the extractor resolves to this id — the view URL and the in-app
138+
// `/workspace/<ws>/files/<id>` path — so a bundled asset never leaves a broken link in the export.
139+
mdContent = mdContent
140+
.replace(new RegExp(`/api/files/view/${escapedId}`, 'g'), () => replacement)
141+
.replace(new RegExp(`/workspace/[A-Za-z0-9-]+/files/${escapedId}`, 'g'), () => replacement)
146142
}
147143

148144
const zip = new JSZip()

0 commit comments

Comments
 (0)