Skip to content

Commit a39178c

Browse files
Bill LeoutsakosBill Leoutsakos
authored andcommitted
Merge staging into EMCN consolidation
2 parents 4881f6e + 96b941f commit a39178c

693 files changed

Lines changed: 252679 additions & 14944 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/global.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,12 @@ Use shared helpers from `@sim/utils` instead of writing inline implementations:
5151
- `structuredClone(value)` — built-in deep clone, no import needed. Never write `JSON.parse(JSON.stringify(obj))`
5252
- `omit(obj, keys)` from `@sim/utils/object` — remove keys from object
5353
- `filterUndefined(obj)` from `@sim/utils/object` — strip undefined-valued keys. Never write `Object.fromEntries(Object.entries(obj).filter(([, v]) => v !== undefined))`
54+
- `isRecordLike(value)` from `@sim/utils/object` — indexable-object guard. Never redeclare `typeof value === 'object' && value !== null && !Array.isArray(value)`
55+
- `toRecord(value)` / `toRecordOrNull(value)` / `toArray(value)` from `@sim/utils/object` — coerce an untyped payload value to a record or array. Never inline `isRecordLike(v) ? v : {}` or `Array.isArray(v) ? v : []`. Where the source is already typed, keep the inline `Array.isArray` check: it narrows, while `toArray` asserts
56+
- `toStringOrNull(value)` / `toNumberOrNull(value)` / `toBooleanOrNull(value)` from `@sim/utils/coerce` — read one scalar out of an untyped payload. Never declare a local one-liner that is byte-identical to one of these (`asString`, `getString`, `nullableString`, …). Keep a local helper that differs: one returning `undefined` rather than `null` changes the wire shape, and one adding `Number.isFinite` or a string parse is a stricter check these deliberately omit
5457
- `truncate(str, maxLength, suffix?)` from `@sim/utils/string` — safe string truncation with ellipsis
58+
- `escapeRegExp(value)` from `@sim/utils/string` — escape regex metacharacters. Never inline `replace(/[.*+?^${}()|[\]\\]/g, '\\$&')`
59+
- `compareStrings(left, right)` from `@sim/utils/string` — code-unit string comparator for hashes, fingerprints, and values compared across processes. Never `localeCompare` there
5560
- `backoffWithJitter(attempt, retryAfterMs, options?)` from `@sim/utils/retry` — exponential backoff with jitter
5661
- `parseRetryAfter(header)` from `@sim/utils/retry` — parse HTTP `Retry-After` header to milliseconds
5762

‎.cursor/rules/global.mdc‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,12 @@ Use shared helpers from `@sim/utils` instead of writing inline implementations:
5454
- `structuredClone(value)` — built-in deep clone, no import needed. Never write `JSON.parse(JSON.stringify(obj))`
5555
- `omit(obj, keys)` from `@sim/utils/object` — remove keys from object
5656
- `filterUndefined(obj)` from `@sim/utils/object` — strip undefined-valued keys. Never write `Object.fromEntries(Object.entries(obj).filter(([, v]) => v !== undefined))`
57+
- `isRecordLike(value)` from `@sim/utils/object` — indexable-object guard. Never redeclare `typeof value === 'object' && value !== null && !Array.isArray(value)`
58+
- `toRecord(value)` / `toRecordOrNull(value)` / `toArray(value)` from `@sim/utils/object` — coerce an untyped payload value to a record or array. Never inline `isRecordLike(v) ? v : {}` or `Array.isArray(v) ? v : []`. Where the source is already typed, keep the inline `Array.isArray` check: it narrows, while `toArray` asserts
59+
- `toStringOrNull(value)` / `toNumberOrNull(value)` / `toBooleanOrNull(value)` from `@sim/utils/coerce` — read one scalar out of an untyped payload. Never declare a local one-liner that is byte-identical to one of these (`asString`, `getString`, `nullableString`, …). Keep a local helper that differs: one returning `undefined` rather than `null` changes the wire shape, and one adding `Number.isFinite` or a string parse is a stricter check these deliberately omit
5760
- `truncate(str, maxLength, suffix?)` from `@sim/utils/string` — safe string truncation with ellipsis
61+
- `escapeRegExp(value)` from `@sim/utils/string` — escape regex metacharacters. Never inline `replace(/[.*+?^${}()|[\]\\]/g, '\\$&')`
62+
- `compareStrings(left, right)` from `@sim/utils/string` — code-unit string comparator for hashes, fingerprints, and values compared across processes. Never `localeCompare` there
5863
- `backoffWithJitter(attempt, retryAfterMs, options?)` from `@sim/utils/retry` — exponential backoff with jitter
5964
- `parseRetryAfter(header)` from `@sim/utils/retry` — parse HTTP `Retry-After` header to milliseconds
6065

‎.github/workflows/ci.yml‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ jobs:
231231
github.event_name == 'push' &&
232232
(github.ref == 'refs/heads/main' || github.ref == 'refs/heads/staging' || github.ref == 'refs/heads/dev')
233233
runs-on: ${{ (vars.CI_PROVIDER == '' || vars.CI_PROVIDER == 'blacksmith') && 'blacksmith-4vcpu-ubuntu-2404' || 'ubuntu-latest' }}
234-
timeout-minutes: 15
234+
timeout-minutes: 30
235235
outputs:
236236
version: ${{ steps.deploy.outputs.deploymentVersion }}
237237
environment: ${{ steps.target.outputs.environment }}
@@ -289,7 +289,7 @@ jobs:
289289
if [ -n "$TRIGGER_BRANCH" ]; then
290290
TARGET_ARGS+=(--branch "$TRIGGER_BRANCH")
291291
fi
292-
bunx trigger.dev@4.5.12 deploy "${TARGET_ARGS[@]}" --skip-promotion
292+
bunx trigger.dev@4.5.16 deploy "${TARGET_ARGS[@]}" --skip-promotion
293293
294294
- name: Validate deployment version output
295295
env:
@@ -643,7 +643,7 @@ jobs:
643643
if [ -n "$TRIGGER_BRANCH" ]; then
644644
TARGET_ARGS+=(--branch "$TRIGGER_BRANCH")
645645
fi
646-
bunx trigger.dev@4.5.12 promote "$VERSION" "${TARGET_ARGS[@]}"
646+
bunx trigger.dev@4.5.16 promote "$VERSION" "${TARGET_ARGS[@]}"
647647
648648
# Build ARM64 images for GHCR (main branch only, runs in parallel with
649649
# tests). Pushes only the immutable sha tag — latest-arm64/version-arm64

‎.github/workflows/test-build.yml‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,19 @@ jobs:
127127
lib/billing/core/usage-log.postgres.test.ts
128128
lib/billing/core/organization-activity.postgres.test.ts
129129
lib/billing/core/usage-analytics-queries.postgres.test.ts
130+
lib/billing/core/organization-usage-pagination.postgres.test.ts
131+
lib/billing/organizations/member-limits.postgres.test.ts
132+
lib/workspaces/organization-workspaces.postgres.test.ts
130133
lib/billing/calculations/usage-reservation.test.ts
131134
135+
- name: Verify access request pagination and impact in PostgreSQL
136+
working-directory: apps/sim
137+
env:
138+
ACCESS_REQUESTS_TEST_DATABASE_URL: postgresql://postgres:postgres@127.0.0.1:5432/sim_access_requests_test
139+
run: |
140+
bun -e 'import postgres from "postgres"; const sql = postgres(process.env.DATABASE_URL); await sql.unsafe("CREATE DATABASE sim_access_requests_test"); await sql.end()'
141+
bunx vitest run ee/access-requests/lib/repository.postgres.test.ts ee/access-requests/lib/impact.postgres.test.ts
142+
132143
- name: Verify fork previews ignore execution file history in PostgreSQL
133144
working-directory: apps/sim
134145
env:

‎CLAUDE.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,12 @@ You are a professional software engineer. All code must follow best practices: a
1616
- `getErrorMessage(e, fallback?)` from `@sim/utils/errors` — extract message string from unknown caught value; never write `e instanceof Error ? e.message : 'fallback'`
1717
- `structuredClone(value)` — built-in deep clone; never `JSON.parse(JSON.stringify(...))`
1818
- `omit(obj, keys)` / `filterUndefined(obj)` from `@sim/utils/object` — object trimming; never `Object.fromEntries(Object.entries(...).filter(...))`
19+
- `isRecordLike(value)` from `@sim/utils/object` — never redeclare `typeof value === 'object' && value !== null && !Array.isArray(value)`
20+
- `toRecord(value)` / `toRecordOrNull(value)` / `toArray(value)` from `@sim/utils/object` — coerce an untyped payload value to a record or array; never inline `isRecordLike(v) ? v : {}` or `Array.isArray(v) ? v : []`. Where the source is already typed, keep the inline `Array.isArray` check: it narrows, while `toArray` asserts
21+
- `toStringOrNull(value)` / `toNumberOrNull(value)` / `toBooleanOrNull(value)` from `@sim/utils/coerce` — read one scalar out of an untyped payload; never declare a local one-liner byte-identical to one of these. Keep a local helper that differs: `undefined` instead of `null` changes the wire shape, and a `Number.isFinite` or string-parse variant is a stricter check these omit
1922
- `truncate(str, maxLength, suffix?)` from `@sim/utils/string` — never inline slice + ellipsis
23+
- `escapeRegExp(value)` from `@sim/utils/string` — never inline `replace(/[.*+?^${}()|[\]\\]/g, '\\$&')`
24+
- `compareStrings(left, right)` from `@sim/utils/string` — code-unit ordering for hashes, fingerprints, and cross-process comparisons; never `localeCompare` there
2025
- `backoffWithJitter(attempt, retryAfterMs, options?)` / `parseRetryAfter(header)` from `@sim/utils/retry` — shared retry pacing; never reimplement exponential backoff inline
2126
- **Deployment flags in the browser**: client code inside a workspace, organization, or standalone settings surface reads `hosted`, `billingEnabled`, `chatEnabled`, and the enterprise feature set through `useDeploymentShape()` (components) or `getDeploymentShape()` (block conditions, stores, helpers) from `@/lib/core/config/deployment-shape`, never `isHosted`/`isBillingEnabled`/... from `env-flags`. The constants freeze at module init from the root layout's `NEXT_PUBLIC_*` transport, which Next's bare 404 shell and `global-error` never emit; the reader is seeded from the server-resolved workspace host context, organization layout, or standalone settings layout instead. Server code keeps reading `env-flags`
2227
- **Package Manager**: Use `bun` and `bunx`, not `npm` and `npx`

‎apps/desktop/src/main/ipc.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import {
2727
type TerminalToolArgs,
2828
} from '@sim/terminal-protocol'
2929
import { getErrorMessage } from '@sim/utils/errors'
30-
import { isRecordLike } from '@sim/utils/object'
30+
import { isRecordLike, toRecord } from '@sim/utils/object'
3131
import { PASTE_LIMITS, utf8ByteLength } from '@sim/utils/paste'
3232
import type { BrowserWindow, IpcMainEvent, IpcMainInvokeEvent, WebContents } from 'electron'
3333
import { clipboard, ipcMain, shell } from 'electron'
@@ -856,7 +856,7 @@ export function registerIpcHandlers(deps: IpcDeps): void {
856856
) {
857857
return { ok: false, error: `Unknown browser tool: ${String(tool)}` }
858858
}
859-
const toolParams = isRecordLike(params) ? params : {}
859+
const toolParams = toRecord(params)
860860
return executeTool(
861861
scope,
862862
tool,
@@ -1532,7 +1532,7 @@ export function registerIpcHandlers(deps: IpcDeps): void {
15321532
) {
15331533
return { ok: false, error: `Unknown terminal tool: ${String(tool)}` }
15341534
}
1535-
const call = isRecordLike(params) ? params : {}
1535+
const call = toRecord(params)
15361536
if (!isTerminalOperation(call.operation)) {
15371537
return { ok: false, error: `Unknown terminal operation: ${String(call.operation)}` }
15381538
}

‎apps/desktop/src/main/local-filesystem.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
} from '@sim/desktop-bridge/local-filesystem-limits'
2020
import { generateId } from '@sim/utils/id'
2121
import { isRecordLike } from '@sim/utils/object'
22+
import { escapeRegExp } from '@sim/utils/string'
2223
import { app, dialog, shell } from 'electron'
2324
import micromatch from 'micromatch'
2425
import safeRegex from 'safe-regex2'
@@ -1114,7 +1115,7 @@ export class LocalFilesystemService {
11141115
regex =
11151116
rawPattern !== undefined
11161117
? new RegExp(expression, ignoreCase ? 'i' : '')
1117-
: new RegExp(expression.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), ignoreCase ? 'i' : '')
1118+
: new RegExp(escapeRegExp(expression), ignoreCase ? 'i' : '')
11181119
} catch {
11191120
// An empty result set would tell the model the string appears nowhere in
11201121
// the user's files — a factual claim it will act on, when in truth the

‎apps/docs/content/docs/api-reference/meta.json‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
"(generated)/files",
1818
"(generated)/knowledge-bases",
1919
"(generated)/workspaces",
20+
"(generated)/organizations",
21+
"(generated)/permission-groups",
22+
"(generated)/access-requests",
2023
"(generated)/workspace-sync",
2124
"(generated)/mcp-servers",
2225
"(generated)/skills",

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ These apply to every command, and may be written before or after it.
4444
| [`sim logs`](/cli/logs) | Manage logs |
4545
| [`sim mcp-servers`](/cli/mcp-servers) | Manage mcp servers |
4646
| [`sim meta`](/cli/meta) | Manage meta |
47+
| [`sim organizations`](/cli/organizations) | Manage organizations |
48+
| [`sim permission-groups`](/cli/permission-groups) | Manage permission groups |
4749
| [`sim sandboxes`](/cli/sandboxes) | Manage sandboxes |
4850
| [`sim secrets`](/cli/secrets) | Manage secrets |
4951
| [`sim selectors`](/cli/selectors) | Manage selectors |

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,8 @@ sim files versions revert <fileId> <version> [options]
299299

300300
| Option | Required | Description |
301301
| --- | --- | --- |
302-
| `--expected-current-version <value>` | No | Revert only while this is still the current version; otherwise the request fails with `409`. Omit to revert whatever is current. Collaborative edits and repeated workflow writes that fold into the current version keep its number. |
302+
| `--expected-current-version <value>` | No | Revert only while this is still the current version; otherwise the request fails with `409`. Omit to revert whatever is current. Collaborative edits and repeated workflow writes that fold into the current version keep its number, so prefer `expectedRevision` to guard content. |
303+
| `--expected-revision <value>` | No | Revert only while the file still holds the content this revision names, as returned by Get File Metadata or an earlier write; otherwise the request fails with `409`. Unlike a version number, it also catches edits that folded into the current version. |
303304

304305
</CommandTable>
305306

@@ -354,6 +355,7 @@ sim files edit <fileId> [options]
354355
| Option | Required | Description |
355356
| --- | --- | --- |
356357
| `--edit <json\|@file>` | Yes | One edit object: &#123;"mode":"search_replace","search":"old","content":"new","replaceAll":false&#125;, &#123;"mode":"replace_between","beforeAnchor":"start line","afterAnchor":"end line","content":"new"&#125;, &#123;"mode":"insert_after","anchor":"line","content":"new"&#125;, or &#123;"mode":"delete_between","startAnchor":"first line deleted","endAnchor":"ending line kept"&#125;. Anchored modes also accept occurrence starting at 1 (JSON, or @path / @- to read a file or stdin). |
358+
| `--expected-revision <value>` | No | Revision from Get File Metadata or an earlier write; the request is refused with `409` when the content moved on. |
357359

358360
</CommandTable>
359361

@@ -613,6 +615,7 @@ sim files set-content <fileId> [options]
613615
| --- | --- | --- |
614616
| `--content <value>` | Yes | Complete replacement content for the file. The 70,000,000-character bound guards the JSON envelope; the decoded bytes must be at most 50 MiB, and a longer base64 payload is rejected with `413`. |
615617
| `--encoding <value>` | No | Content encoding. Accepted values: `utf-8`, `base64`. |
618+
| `--expected-revision <value>` | No | Revision from Get File Metadata or an earlier write; the request is refused with `409` when the content moved on. |
616619

617620
</CommandTable>
618621

0 commit comments

Comments
 (0)