mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-24 15:45:35 +08:00
ddab1aaa1cedb2cae63cbfb700202d04115f06bb
58
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
648a5a117d |
feat(storage): support S3-compatible endpoints (R2, MinIO, B2) for file storage (#4865)
* feat(storage): support S3-compatible endpoints (R2, MinIO, B2) for file storage Add S3_ENDPOINT and S3_FORCE_PATH_STYLE env vars, wired into the shared upload S3 client so Cloudflare R2, MinIO, Backblaze B2, and other S3-compatible stores work for self-hosted file storage. The endpoint is trusted operator config (no SSRF/HTTPS gate). Makes the multipart Location fallback endpoint-aware, extends the S3 client unit tests, and documents the new vars in Helm values, .env.example, and the English self-hosting docs (incl. browser-reachability + CORS guidance). * docs(storage): add RustFS as an S3-compatible provider example * fix(storage): address review feedback and fix env mock for CI - Add envBoolean to the shared env test mock (createEnvMock) so config.ts's forcePathStyle coercion resolves — fixes failing knowledge/utils.test.ts - Declare S3_FORCE_PATH_STYLE as z.string() (every other env var's pattern); it's coerced via envBoolean at the consumption site, avoiding a boolean type that never matches the string process.env value - Log path-style from S3_CONFIG.forcePathStyle (envBoolean) instead of a separate isTruthy call, so the startup log can't disagree with the client - Make buildObjectFallbackUrl honor forcePathStyle: virtual-hosted-style URL (bucket as subdomain) for R2, path-style only when forcePathStyle is set * docs(storage): add backlinks to S3-compatible providers (R2, MinIO, Ceph, B2, RustFS) and backends |
||
|
|
3f3efc98c3 | chore(auth): remove deprecated OAuth MCP provider plugin and backing tables (#4847) | ||
|
|
a7b0bd311d |
fix(deps): upgrade vitest to ^4.1.0 to patch critical Vitest UI advisory (GHSA-5xrq-8626-4rwp) (#4837)
* fix(deps): upgrade vitest to ^4.1.0 to patch critical Vitest UI advisory (GHSA-5xrq-8626-4rwp) - Bump vitest and @vitest/coverage-v8 to ^4.1.0 across all workspaces (only patched release for the critical 'Vitest UI server arbitrary file read/execute' advisory; no 3.x backport exists) - Widen @sim/testing peer range to ^3.0.0 || ^4.0.0 - Migrate constructor mocks to class expressions: vitest 4 uses Reflect.construct for mocks invoked with new, and arrow/function implementations are not constructable (function expressions also get reverted to arrows by biome's useArrowFunction) - Remove deprecated test.poolOptions from apps/sim/vitest.config.ts (options are now top-level in vitest 4) * fix(deps): exclude vulnerable vitest 4.0.x from @sim/testing peer range Tighten the v4 arm of the peer range to >=4.1.0 <5.0.0 so the peer requirement cannot be satisfied by the unpatched 4.0.x builds that GHSA-5xrq-8626-4rwp affects. * fix(testing): make vitest 4 constructor mocks type-check cleanly - logging-session & mcp-oauth mocks: a class passed to mockImplementation has a construct signature that isn't assignable to its (...args) => any parameter, failing tsc. Use named function declarations instead (constructable via Reflect.construct, assignable to mockImplementation, and not rewritten to arrows by biome's useArrowFunction). - database.mock.ts: vitest 4's generic vi.fn typings no longer break the self-referential cycle on the transaction callback's tx param; loosen tx and annotate the callback's return type to resolve the implicit-any errors. * test(isolated-vm): de-flake queue-capacity scheduler tests The 'queue is full' and 'per-owner queued limit' tests relied on 'await sleep(1)' to assume the first request had reached the queue before submitting the overflow request. The first request only enqueues after an async spawn-failure chain (acquireWorker -> spawn exit -> resolve null -> enqueue), which isn't guaranteed within 1ms under CI load — the overflow request then found an empty queue and hit the 200ms queue-wait timeout instead of the capacity rejection. Replace the wall-clock barrier with a deterministic, event-driven one: hold the single global concurrency slot (IVM_MAX_CONCURRENT=1) with an active worker and await an explicit 'dispatched' signal (fired when the worker receives its execute message, after the scheduler counts it active). The follow-up requests then deterministically hit the synchronous enqueue path. Also drops the queue-wait timeout from 200ms to 50ms, so the tests run faster. |
||
|
|
fd77bb4069 |
feat(copilot): add seq ordinal to copilot_messages for order-preserving reads (#4791)
copilot_messages had no column preserving message order: created_at (set from each message's timestamp) ties at millisecond granularity in 58% of chats, and some chats have out-of-order timestamps within their array. The only other tiebreaker, id, is a random UUID — so ORDER BY created_at, id renders same-timestamp user/assistant pairs swapped. This blocks the R+1 read cutover. Add an integer seq = the message's 0-based index within the chat's JSONB array (ground-truth order), backfilled inline in migration 0219 (no script for self-hosters or us). Reads will use ORDER BY seq NULLS LAST, created_at, id at cutover; reads still come from JSONB after this PR. Design: - seq is a tiebreaker, not the sole sort key (concurrent-append/NULL safety). - Nullable now; defer NOT NULL so rolling-deploy old pods don't fail inserts. - replace (update-messages snapshot) overwrites seq = array index (re-densifies after a mid-conversation delete); append preserves existing seq via COALESCE and assigns base+idx from a single MAX(seq) read (never MAX+i in SQL — multi-row batches would collide). The non-atomic read-then-insert window is documented and bounded by the read tiebreak + snapshot re-densify. - Dedupe message ids before insert (87 prod chats carry dup ids; a repeated id in one INSERT...ON CONFLICT would otherwise throw). - Backfill picks first-occurrence per (chat,id), gap-free via ROW_NUMBER; validated on staging data (0-based, contiguous, 0 bad ranges). Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
ddc47eb221 |
feat(copilot): add copilot_messages table with dual-write rollout (#4726)
Splits copilot chat messages out of the copilot_chats.messages JSONB column into a dedicated copilot_messages table. JSONB stays canonical during R+0 — every write path dual-writes to the new table best-effort (try/catch + log warn, never throws). Migration 0217 creates the table + indexes and inline-backfills history from JSONB so OSS self-hosters don't need to run a separate script. Write paths covered: - post.ts (user message append) - terminal-state.ts (assistant turn finalize) - update-messages/route.ts (snapshot replace) - inbox/executor.ts (background turn) - fork/route.ts (chat clone) - superuser/import-workflow/route.ts (chat import) Each call threads chatModel + streamId where relevant; ON CONFLICT DO UPDATE preserves existing stream_id / model via COALESCE. For pre-R+1 reconciliation, run: bun apps/sim/scripts/copilot-messages-reconcile.ts [--since='7 days'] Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
59792c052f |
improvement(mcp): bound MCP memory and lifecycle concurrency (#4751)
* improvement(mcp): bound MCP memory and lifecycle concurrency * update db mock * address comments * address comments |
||
|
|
209ca5f121 |
fix(large-refs): cleanup based on table read (#4716)
* fix(large-refs): cleanup based on table read * address comments * address comments * bubble up storage ref errors * cleanup code * do not attempt blob deletion for infra outage * cleanup dup helper |
||
|
|
46db40620f |
feat(mcp): OAuth 2.1 + PKCE for outbound MCP servers (#4441)
* feat(mcp): OAuth 2.1 support for outbound MCP servers
* fix(mcp): tighten OAuth refresh race and session-error detection
Re-load the OAuth row inside withMcpOauthRefreshLock so concurrent
callers observe predecessor-written tokens instead of a stale snapshot
loaded before lock acquisition. Without this, the second caller's
provider held a rotated-out refresh token and the SDK tripped
invalid_grant, forcing reauthorization.
Switch isSessionError to match the SDK's typed StreamableHTTPError
(code 404/400) instead of substring-checking arbitrary error messages,
removing false positives on URLs that happen to contain those digits.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* refactor(mcp): tighten OAuth callback contract and registration metadata
- Validate callback query params via mcpOauthCallbackContract instead of
raw searchParams.get, matching the rest of the MCP route surface.
- Drop non-RFC-7591 application_type field from dynamic client registration
to avoid rejection by strict authorization servers.
- Collapse the pre-lock OAuth row load in createClient — the row is now
loaded exclusively inside withMcpOauthRefreshLock, removing a redundant
query and a stale-snapshot path.
* fix(mcp): narrow workspaceId before async closure in OAuth createClient
* fix(mcp): return authType from create-server endpoint
The POST /api/mcp/servers handler omitted authType from the success
response, so useCreateMcpServer always saw data.data.authType as
undefined and never triggered the OAuth popup after creating an
OAuth-protected server. Thread authType through performCreateMcpServer
into the response so the client can decide whether to auto-start OAuth.
* fix(mcp): mirror server null normalization in optimistic oauthClientId update
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(mcp): revert optimistic oauthClientId to undefined to match McpServer type
The response contract preprocesses null → undefined, so McpServer.oauthClientId
is string | undefined. Using null broke type checking.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(mcp): tighten OAuth probe signal and clear stale popup interval
- probe: only classify as OAuth on resource_metadata or scope params.
Bare `Bearer error="invalid_token"` is generic and used by API-key servers,
so it must not auto-flip the auth type to OAuth.
- popup hook: clear any existing close-watcher interval before overwriting
when startOauthForServer is invoked twice for the same serverId.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(mcp): normalize empty-string oauthClientId at route boundary
Orchestration already converts falsy → null via `|| null` (server-lifecycle.ts),
so the DB was never receiving an empty string. Tightening the route layer to
match the same convention keeps the boundary contract consistent and avoids
relying on downstream normalization.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* feat(canvas): expand MCP tool params into per-row labels on block tile
The MCP Tool block on the workflow canvas previously crammed every selected-
tool parameter into a stringified blob under the `Tool` row. Now, when a tool
is selected, the tile reads the cached `_toolSchema` and emits one labeled
SubBlockRow per parameter (matching the Exa block's per-param layout). Labels
reuse `formatParameterLabel` for parity with the editor panel; values pass
through the existing `getDisplayValue` so booleans/numbers/arrays render
identically to other blocks. Deterministic tile height counts expanded rows
so the tile sizes correctly.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* feat(logs): show MCP icon and strip prefix in trace tool spans
Tool spans for MCP calls were rendering the raw id (e.g.
`mcp-f908f259-planetscale_list_organizations`) with the default blank-
square icon. Now they read just the tool name and render the MCP block's
icon and bgColor, matching how workflow-execute tools render.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(logs): lift near-black trace icon backgrounds for dark-mode contrast
Block bgColors below a small luminance threshold (e.g. the MCP block's
#181C1E) rendered nearly invisible against the dark-mode surface
(--bg: #1b1b1b). Adds a tiny adjustBgForContrast helper that floors each
RGB channel at 0x33 only when luminance is below 30,000, leaving every
branded color above that band untouched. Applied to both the trace tree
row and the detail pane.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(logs): fall back to neutral gray for near-black trace icon bgs
#333333 was still too close to the dark-mode surface to read. For bgs
below the luminance threshold (e.g. the MCP block's #181C1E) we now fall
back to DEFAULT_BLOCK_COLOR (#6b7280) — the same neutral the renderer
uses for blocks with no distinct identity. Clearly visible in both
themes; brighter brand colors still pass through.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* chore(db): drop 0209_mcp_oauth migration ahead of staging merge
Staging shipped 0209_smiling_fixer; the MCP OAuth migration will be
regenerated on top of staging as 0210.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* chore(db): regenerate MCP OAuth migration as 0210
Re-runs drizzle-kit generate on top of staging's 0209_smiling_fixer.
Same schema (mcp_server_oauth table + mcp_servers.auth_type / oauth_*
columns) as the dropped 0209_mcp_oauth.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* chore(audit): bump route baseline 748 → 749 after staging merge
The post-merge route count is 749 (this branch's OAuth start/callback
plus staging's new route). I had set the baseline to 748 in the merge
conflict resolution — bumping to match reality so the strict audit
passes.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* chore: remove source-command skill files committed by accident
These were untracked-then-accidentally-staged in
|
||
|
|
f0311a6f5e |
feat(table): chunked dispatcher + workflow cascade (#4672)
* feat(table): chunked dispatcher for workflow-column runs
Replaces the all-rows-at-once runWorkflowColumn with a row-window dispatcher
backed by a new table_run_dispatches row. Each user click inserts a dispatch
row and triggers a trigger.dev task that crawls the table 20 rows at a time,
re-enqueueing itself between windows. The HTTP/Mothership entrypoints return
{ dispatchId } immediately instead of holding the request open for minutes
on multi-thousand-row dispatches.
- Per-row cancel stamps cancelledAt; the dispatcher skips cells whose
cancelledAt > dispatch.requestedAt so a mid-cascade cancel sticks even
under isManualRun.
- Table-wide cancel marks active dispatches cancelled atomically so the
dispatcher bails on its next iteration.
- New 'dispatch' SSE event variant plumbed; client ignores for v1.
* fix(table): eager bulk clear on column run so cells flip immediately
Run-column with run-mode 'all' wasn't visually flipping rows that already
had data — the cell renderer's "value wins" branch kept showing the prior
output behind the queued/running state. The dispatcher only cleared one
window of rows at a time, so most of the column stayed stale until the
cursor walked to it.
Now:
- Dispatcher's `pending → dispatching` transition runs a single SQL UPDATE
that wipes targeted `data` output columns and `executions[gid]` across
every targeted row (mode-aware: 'incomplete' skips fully-filled rows).
- Per-window clear in `dispatcherStep` is gone — rows are pre-cleared,
the loop only filters cancel tombstones / unmet deps and enqueues.
- Optimistic patch in `useRunColumn` mirrors the bulk clear by nulling
output values in the cached row, so the UI flips queued/running
instantly without waiting for the SSE catch-up.
* fix(table): bulk clear honors in-flight execs under mode: 'incomplete'
The eager bulk clear for mode: 'incomplete' only skipped rows that were
already fully filled, so two overlapping dispatches could race — dispatch B
would nuke executions[gid] on a row dispatch A had just stamped 'queued',
flickering the cell and potentially confusing the worker.
Skip any row whose targeted group is currently queued/running/pending — an
'incomplete' run shouldn't touch what another dispatch is actively working
on. The per-walk 'in-flight' eligibility skip already handles rows that
flip in-flight between the clear and the cursor reaching them.
* refactor(table): dispatcher uses batchTriggerAndWait + tag-based cancel
Switch the per-window cell fan-out from fire-and-forget tasks.trigger to
tasks.batchTriggerAndWait. The dispatcher is now a single long-lived
trigger.dev task that loops dispatcherStep until the table is exhausted;
trigger.dev CRIU-checkpoints the parent during each wait so we don't pay
compute while cells execute. Queue depth is bounded at WINDOW_SIZE per
dispatch — no more flooding trigger.dev with a million queued runs.
- dispatcher.ts builds payloads via the new shared buildPendingRuns helper
and calls tasks.batchTriggerAndWait directly. Pre-stamps each cell to
`queued` (jobId=null) so the UI flips instantly.
- table-run-dispatcher.ts is now a plain while-true loop. No
RUN_BUDGET_MS, no self-re-enqueue, no cold-start tax per window.
Cancel:
- New cancelCellRunsByTags(tags) paginates runs.list + runs.cancel(id).
- cancelWorkflowGroupRuns fires the tag-sweep alongside the per-jobId
queue.cancelJob path (preserved for auto-fire cells that have real
jobIds from single tasks.trigger calls).
- Trigger.dev acks the cancel → batchTriggerAndWait resumes → dispatcher
observes the dispatch-row cancel flag → exits.
Side fixes:
- getAsyncBackendType returns 'trigger-dev' whenever taskContext.isInsideTask
is true, regardless of TRIGGER_DEV_ENABLED env. The preview/dev-sim
worker silently routing cell jobs to DatabaseJobQueue (no poller) is
fixed without any env config change.
- runWorkflowColumn skips the dispatcher entirely when trigger.dev is
disabled, running cells inline via DatabaseJobQueue.runInline. HTTP
response returns dispatchId: null in that mode.
- runColumnContract response schema updated to dispatchId.nullable().
* fix(table): show Stop button on optimistic-pending row cells
isExecInFlight required a jobId for `pending` status, gating it as "real
backend pending" vs "optimistic flag only." The row-gutter Stop button
keyed on this — so a freshly clicked Play sat as `pending` (no jobId) and
the user couldn't cancel it until the server-side `queued` stamp arrived
via SSE. With the dispatcher pre-batch stamping cells as `queued` (not
`pending`) and no per-cell jobIds under batchTriggerAndWait, the gap was
worse.
Drop the jobId requirement. `pending` now counts as in-flight everywhere.
Cancel writes `cancelled` to the cell exec authoritatively whether or not
a real trigger.dev run exists yet — cancelling an optimistic cell means
"don't run this," which is correct.
Also collapse isOptimisticInFlight into isExecInFlight since the two
helpers are now identical.
* refactor(table): loop-in-cell cascade + dispatcher-everywhere routing
Two coupled changes:
1. Cell-task runs the row's full cascade in-process. executeWorkflowGroupCellJob
acquires a Redis lock per (tableId, rowId) with heartbeat (10s/30s TTL),
then loops through eligible workflow groups for the row. One cell-task =
one row's full cascade, not N. Resume worker holds the same lock and
continues the cascade after a HITL resume. Shared withCascadeLock helper
in lib/table/cascade-lock.ts.
2. Every cell-enqueue goes through the dispatcher. The implicit
scheduleRunsForRows reactor in service.ts is removed — 8 callsites
(insertRow, batchInsertRows, upsertRow, updateRowsByFilter,
batchUpdateRows, addWorkflowGroup, updateWorkflowGroup) now fire
runWorkflowColumn with mode: 'incomplete', isManualRun: false. HTTP
routes that call updateRow directly also fire runWorkflowColumn
afterwards. scheduleRunsForTable / scheduleRunsForRowIds deleted;
scheduleRunsForRows demoted to private (only the TRIGGER_DEV_ENABLED=false
fallback uses it). skipScheduler flag dropped from UpdateRowData /
BatchUpdateByIdData — no longer meaningful since there's nothing implicit
to suppress.
Plumbed isManualRun through the dispatch row (new is_manual_run column,
default true) so auto-fire callers honor autoRun: false and don't re-run
completed cells.
Stamp 'pending' (not 'queued', executionId: null) before
batchTriggerAndWait — cell-task writes its own 'queued' on lock acquire.
Small UI polish: row gutter Play button spacing, "Delete workflow" →
"Delete column" label, optimistic-pending cells now show Stop button
(isExecInFlight no longer requires jobId).
* fix(table): SQL cancellation guard allows worker to claim a null-execId cell
The dispatcher's pre-batch `pending` stamp leaves executionId unset so any
cell-task that wins the cascade lock can claim the cell. The cancellation-
guard SQL clause was rejecting these claims because it tested
`executions->gid IS NULL` (whole exec missing) but the pre-stamp leaves
the exec present with executionId=null.
Add a third carve-out: `executions->gid->>'executionId' IS NULL`. Now the
guard reads "write allowed if no exec exists, OR no executionId is set
yet, OR the executionId matches ours."
Symptom: every cell-task's first markWorkflowGroupPickedUp call would log
"SQL guard saw cancelled" and skip, leaving cells stuck at the dispatcher's
pending stamp.
* fix(table): dispatcher cursor starts at -1 so position 0 is included
The dispatcher's row-window SELECT is `position > cursor` for exclusive
lower-bound semantics. With cursor initialized to 0, position-0 rows were
never picked up — every dispatch silently skipped the table's first row.
Start cursor at -1 instead. First window's filter `position > -1` matches
position 0; subsequent iterations advance to `lastPosition` which then
correctly excludes already-processed rows.
* refactor(table): align optimistic UI with new dispatcher; sticky cancel via 'new' mode
Fix 0: new `DispatchMode = 'new'` for auto-fire callsites. Eligibility skips
rows with any prior `executions[gid]` entry — cancelled / errored / completed
cells stay sticky until a manual run. Dispatcher's windowed SELECT pushes
`NOT jsonb_exists_any(...)` to SQL so CSV imports into mostly-attempted
tables don't pay a per-window load+JS-filter. `batchInsertRows` drops its
`rowIds` payload (keeps dispatch scope tiny on big imports).
Fix A/B/D: client optimistic patches now mirror the backend's actual
invariants. `useCreateTableRow.onSuccess` stamps eligible groups via
`optimisticallyScheduleNewlyEligibleGroups` so newly-inserted rows show
`Queued` instantly. `useCancelTableRuns.onMutate` distinguishes optimistic-
only pending (`executionId == null` — strip silently) from real worker
claims (stamp cancelled; SSE will reconcile). Drop `onSettled` invalidation
on `useUpdateTableRow` / `useBatchUpdateTableRows` to kill the
delete-cell flicker.
Fix C: active-dispatches overlay. New `listActiveDispatches` helper,
contract, and `GET /api/table/[tableId]/dispatches` route. `kind:'dispatch'`
SSE events carry scope+cursor+mode on every transition. New
`useActiveDispatches` hook + `resolveCellExec` synthesize a virtual
`pending` exec for cells in an active dispatch's scope ahead of cursor —
queued indicators now survive page refresh during long Run-all dispatches.
`cancelWorkflowGroupRuns` emits `kind:'dispatch',status:'cancelled'`
events so the overlay clears without a refetch.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor(table): unify trigger.dev and inline dispatcher paths
`runWorkflowColumn` now always inserts a `table_run_dispatches` row and
drives the dispatcher state machine. The trigger.dev / in-process branch
narrows to a single line: trigger.dev fires `tableRunDispatcherTask` (which
calls the new `runDispatcherToCompletion`), the inline path calls the same
helper fire-and-forget. Deletes `scheduleRunsForRows` and
`stampQueuedOrCancel` — the inline-fallback no longer duplicates window
walking, SSE emission, or cancel.
The dispatcher's window-execute call goes through `JobQueueBackend`:
- New `batchEnqueueAndWait` interface method.
- Trigger.dev impl wraps `tasks.batchTriggerAndWait` behind a
`taskContext.isInsideTask` guard (clear error if called from outside a
task).
- Database impl skips `async_jobs` entirely — `Promise.all` over
`options.runner(payload, signal)` per item, with per-cell AbortControllers
tracked by `cancelKey` for cancel.
`cancelInlineRun` moves to the interface as `cancelByKey` so
`cancelWorkflowGroupRuns` no longer reaches into the database backend.
Fix `mode: 'new'` SQL filter:
- `${array}::text[]` interpolated as a tuple-cast which Postgres rejected
("cannot cast type record to text[]") and every inline dispatch silently
failed. Switched to `ARRAY[${sql.join(...)}]::text[]`.
- Predicate was `jsonb_exists_any` ("any one targeted group present"),
which excluded rows that needed at least one group re-run after a
downstream output was deleted. Switched to `jsonb_exists_all` — per-group
JS eligibility handles the rest.
Cascade-loop workflowId bug: `runRowCascadeLoop` was not threading the new
group's `workflowId` when advancing across groups. The cell-task ran the
previous group's workflow against the next group's cell, terminating
`completed` with empty `accumulatedData`. Fixed by tracking
`currentWorkflowId` alongside `currentGroupId` / `currentExecutionId`.
Client optimistic-patch tightening:
- `useRunColumn.onMutate` mirrors server eligibility — skip cells with
unmet deps so unmet rows don't flash Queued and get stuck (no SSE will
arrive for cells the server skipped).
- `resolveCellExec` overlay synthesizes a virtual `pending` only when
`areGroupDepsSatisfied` is true. Rows with unmet deps render Waiting,
matching the dispatcher's actual behavior.
Cleanup from /simplify pass:
- Use `generateShortId(20)` instead of
`generateId().replace(/-/g, '').slice(0, 20)`.
- Inline `batchEnqueueAndWait` no longer allocates synthetic ids
(returned `string[]` is unused).
- Flattened the per-cell `tracked` array — only push entries that
registered controllers, drop the null placeholders.
- Extracted `runDispatcherToCompletion` to share the loop between the
trigger.dev wrapper and the in-process path.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(table): backend running counter, dep-aware retrigger, sidebar polish
Counter (Fix 1): top-right "X running" + per-row badge are now
backend-bootstrapped via a count on `user_table_rows.executions ->> 'status'
= 'running'` returned alongside active dispatches. SSE `kind: 'cell'` events
compute a delta from `prev → next` status to keep the cache live; cell
events for rows outside the loaded page slice trigger a run-state refetch.
On `pruned` we invalidate the cache. Counts only worker-claimed `running`
cells — optimistic queued/pending no longer inflate the badge, and rows
outside the loaded page slice are counted too.
Sidebar (Fix 2 + 3a): `Run after` no longer ticks every column by default
for new groups (empty list). Save is disabled with an inline error when
auto-run is on with zero deps. `edit-group` mode anchors the left-of-current
filter to the group's leftmost column, so a workflow can only depend on
columns to its left.
Reorder scrub (Fix 3b): `updateTableMetadata` walks the schema's workflow
groups when `columnOrder` is in the patch and drops any dep whose new
position lands at or after the group's leftmost column (uses the existing
`stripGroupDeps` helper). Metadata + schema updates land atomically.
Server returns ordered columns (Fix 3b cont'd): `getTableById` /
`listTables` now sort `schema.columns` by `metadata.columnOrder` before
returning, via a new `applyColumnOrderToSchema` helper. Every consumer
(grid, sidebar, copilot, mothership) gets one ordered list — the sidebar's
leftmost-group-column anchor now points at the right index.
Dep-aware retrigger (Fix 4): editing a value that a downstream workflow
depends on now re-runs that workflow.
- `deriveExecClearsForDataPatch` returns
`{ executionsPatch, inFlightDownstreamGroups }`. Walks
`schema.workflowGroups[].dependencies.columns` for every column in the
patch, clears terminal-state downstream entries, and reports in-flight
entries.
- `updateRow` calls `cancelWorkflowGroupRuns` + `runWorkflowColumn`
(`mode: 'incomplete' + isManualRun: true`) for in-flight downstream
groups, then always fires `runWorkflowColumn({ mode: 'new' })` for the
cleared groups. Skips both when `executionsPatch` is provided by the
caller — those are cell-task / cancel writes that would otherwise spawn
a recursive flood of dispatches per partial-write.
- `cancelWorkflowGroupRuns(tableId, rowId, { groupIds? })` accepts a
per-group filter so the cancel only touches the affected groups, not
every in-flight cell on the row.
- `pickNextEligibleGroupForRow` now treats a dispatcher pre-stamp
(`pending` + `executionId: null`) as claimable — the cascade-loop is the
real owner. Without this, the dispatcher's pre-stamp of downstream
groups made the cascade-loop see them as "in-flight" and skip them,
stranding `pending` cells forever.
- `optimisticallyScheduleNewlyEligibleGroups` extends the cache patch to
flip dep-touched groups to `pending` regardless of their current status,
matching the server's cancel-then-rerun behavior.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(table): paused workflow cells route through executeResumeJob; render Pending + viewable
Three connected issues with workflows that pause mid-cell (e.g. wait blocks):
1. `/api/resume/poll` (the time-pause auto-resumer) called
`PauseResumeManager.startResumeExecution` directly, bypassing
`executeResumeJob` from `background/resume-execution.ts`. The wrapper is
where the cell-context restoration + cascade-loop continuation lives —
without it, the resumed workflow ran to completion but never wrote the
terminal state back to the table cell. Cell stays `pending` forever
even though the underlying execution finished.
Fix: dynamically import `executeResumeJob` and use it for the
`'starting'` branch. Same primitive the trigger.dev `resumeExecutionTask`
wraps — calling it directly handles both trigger.dev-disabled local dev
and trigger.dev-enabled prod identically.
2. The cell renderer mapped `status: 'pending'` to `kind: 'queued'` (gray
"Queued" badge) regardless of whether the run had started. A HITL-paused
run has `status: 'pending'` + `jobId` prefixed `paused-` + a real
`executionId` — semantically very different from "queued, hasn't run."
Now renders as `pending-upstream` (the existing Pending pill) for
paused-jobId rows.
3. Right-click "View execution" was disabled for `pending` cells (gated to
`completed | error | running`), so users couldn't open the trace for a
paused execution. Paused runs have a viewable trace (the executionId is
real and the log row exists). Both the per-row context menu and the
action-bar derivation now recognize `pending` + `paused-` jobId as a
started run.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(table): typewriter reveal for SSE-driven workflow cell values
Workflow-output cells now reveal their text character-by-character when an
SSE update lands, while page reloads and virtualization remounts still paint
the value instantly. A first-render guard inside the new useTypewriter hook
distinguishes hydration from live updates with no plumbing through the cell
tree.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(table): address bugbot/greptile review feedback
Two P1 issues + one cleanup from the bot reviewers:
1. **Double-dispatch + completed-output wipe.** Both PATCH row routes
(`app/api/table/[tableId]/rows/[rowId]` and
`app/api/v1/tables/[tableId]/rows/[rowId]`) were firing a second
`runWorkflowColumn({ mode: 'incomplete' })` after `updateRow` returns.
`updateRow` already fires `mode: 'new'` internally for user edits, so
the second call created a concurrent dispatch. Worse, the
`mode: 'incomplete'` path's `bulkClearWorkflowGroupCells` wipes ALL
targeted output columns on any row where any one column is empty —
meaning sibling-group completed outputs could be erased. Removed both
route-level calls; auto-dispatch lives entirely in `updateRow`.
2. **`runWorkflowColumn` log-spamming on plain tables.**
`if (targetGroups.length === 0) throw new Error(...)` fired on every
row insert/update for tables without any workflow groups (the
majority). Every caller wraps with `.catch(logger.error)`, so each
PATCH produced an error-level log. Return `{ dispatchId: null }`
silently — manual `runWorkflowColumn` callers pass `groupIds`
explicitly so they can't reach this branch.
3. **`isManualRun` plumbed through dispatch SSE events.** Late-arriving
`kind: 'dispatch'` events for dispatches not in the initial fetch
were hardcoding `isManualRun: false`. Added the field to the event
shape, emit it from `dispatcherStep` (pending → complete, dispatching
transitions) and `markActiveDispatchesCancelled`, and consume it in
the SSE handler with a sensible fallback for legacy emits.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor(table): row executions sidecar + left-to-right dep retrigger + cancel counter refresh
Split per-row workflow-group execution state out of the user_table_rows.executions
JSONB column into a new table_row_executions sidecar keyed by (row_id, group_id).
Dispatcher filters, "X running" counter, bulk clears, and the cancellation guard
all hit indexed columns instead of walking JSONB. Wire shape unchanged — server
merges sidecar rows back into row.executions on the way out.
Also:
- deriveExecClearsForDataPatch now walks workflowGroups left-to-right with a
propagating dirtied-column set so transitive dep chains (edit col A → group 1
re-runs → group 2 depends on group 1's output → group 2 re-runs) collapse to
a single forward pass.
- useCancelTableRuns.onSettled invalidates the activeDispatches query so the
top-right counter and row gutter Stop button refetch from the server after
any Stop (per-cell, row, or table-wide). countRunningCells is the source of
truth; client no longer needs duplicate state.
Three migrations on this branch (0209 + 0210 + new sidecar) collapsed into one
since the feature is unreleased.
* fix(table): address remaining cursor/greptile review feedback
- Mothership update_row no longer double-dispatches. updateRow already fires
the auto-cascade internally; the second `mode: 'incomplete'` call here
raced with it and could bulk-clear sibling-group outputs.
- SSE dispatch events no longer dropped when the activeDispatches cache is
cold. Seed an empty TableRunState if the initial fetch hasn't landed yet
so the queued overlay doesn't lose the first dispatch event.
- batchUpdateRows now runs cancel+rerun for per-row in-flight downstream
groups, mirroring updateRow. Without this, dep edits in a batch left
running workflows reading stale upstream values.
* fix(table): cancel prior runs, scope batch insert dispatch, recover orphan pre-stamps
Addresses cursor + greptile review feedback on table dispatcher edge cases:
- Manual table-wide Run-all / Run-column now cancels prior active dispatches
AND in-flight cell workers before bulk-clearing. Without this, mode:'all'
deleted running sidecar rows out from under their workers (which kept
writing into the wiped state) and a second Run-all could enqueue overlapping
cells racing on the same rows. Row-scoped manual calls (dep-edit cascade)
are excluded — those already cancel their own scope.
- batchInsertRowsWithTx now scopes its auto-dispatch to the newly-inserted
row ids. Without this, after the sidecar migration the NOT EXISTS filter
matches every existing row (zero sidecar entries), so a CSV import would
walk the entire table dispatching workflow runs on every pre-existing row.
- classifyEligibility carve-out: pending + executionId=null is an orphan
pre-stamp (cascade-lock contention, batchEnqueueAndWait failure, etc.),
treated as claimable so future dispatchers can re-stamp instead of skipping
it as 'in-flight' forever. Matches pickNextEligibleGroupForRow's logic.
- On batchEnqueueAndWait failure, dispatcherStep now sweeps the orphan
pre-stamps it wrote for the failed batch so the cells don't render Queued
forever; the next user action picks them up cleanly.
* fix(table): row-scoped Refresh cancels in-flight; counter includes queued/pending
- runWorkflowColumn now cancels prior in-flight cells for row-scoped manual
runs too (context-menu Refresh on a row subset, action-bar Refresh on
selected rows). Previously only the table-wide path cancelled, so a
row-scoped Refresh would bulk-clear running sidecar rows without aborting
workers. Per-row cancel skips markActiveDispatchesCancelled so unrelated
dispatches keep running.
- countRunningCells now counts all in-flight statuses (queued / running /
pending) instead of just running. The row gutter Run/Stop button reads
this map — with the old behavior, clicking Play during the queued window
would re-enqueue an already-queued cell. SSE applyCell handler updated
to use isExecInFlight so client deltas track the same semantics.
* fix(table): per-row Stop tombstones ahead-of-cursor rows during Run-all
Per-row Stop only cancelled sidecar rows already in flight. A row the
dispatcher hadn't reached yet had no exec record, so Stop was a no-op there
— the dispatcher would later walk to it, classify the group eligible, and
re-fire workflows the user thought they stopped.
cancelWorkflowGroupRuns now, for a per-row cancel, checks active dispatches
whose scope covers the row and writes `cancelled` tombstones (cancelledAt =
now) for the at-risk groups that don't already have a sidecar entry. The
dispatcher's existing `cancelledAt > dispatch.requestedAt` filter then skips
them when the cursor arrives. onConflictDoNothing guards against clobbering
a concurrently-written entry; the active-dispatch check avoids stamping
spurious cancels on idle rows.
* fix(table): seed dispatch overlay on Run; surface batch-enqueue failures as error
- useRunColumn.onSuccess invalidates the activeDispatches query so the
resolveCellExec queued overlay populates immediately for ahead-of-cursor
rows (scrolled-in / refetched), instead of waiting for the first dispatch
SSE. Targeted at activeDispatches only — the rows cache stays owned by
useTableEventStream.
- On batchEnqueueAndWait failure, dispatcherStep now flips the orphan
pre-stamps to a terminal `error` state and emits a cell SSE event, rather
than deleting them. The cursor still advances past the window, but the
dropped cells are now visible (Error pill) instead of silently empty, stay
out of the in-flight set, and re-run on the next manual run.
* fix(table): seed dispatch overlay on Run; surface batch-enqueue failures as error
- useRunColumn.onSuccess invalidates activeDispatches so the resolveCellExec
queued overlay populates immediately for ahead-of-cursor rows instead of
waiting for the first dispatch SSE. Rows cache stays owned by SSE.
- On batchEnqueueAndWait failure, dispatcherStep flips orphan pre-stamps to a
terminal error state (+ cell SSE) instead of deleting them, so the dropped
window is visible (Error pill) rather than silently empty and re-runs on the
next manual run.
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
8d7bbbc670 |
chore(utils): migrate to shared random/ID utilities and add enforcement linting (#4623)
* chore(utils): migrate to shared random/ID utilities and add enforcement linting - Replace all Math.random(), crypto.randomUUID(), crypto.randomBytes(), nanoid, and uuid usages with shared @sim/utils/random and @sim/utils/id helpers across 72 files - Add new @sim/utils exports: deepClone, omit, filterUndefined (object), truncate (string), backoffWithJitter, parseRetryAfter (retry), getErrorMessage (errors) - Sweep all getErrorMessage, sleep, deepClone callsites across 500+ files to use shared utilities - Add Biome noRestrictedImports rule to catch nanoid, uuid, and crypto named imports at lint time - Add scripts/check-utils-enforcement.ts to catch Math.random and crypto.* global property access - Add check:utils script to package.json * chore(utils): replace deepClone wrapper with structuredClone built-in deepClone() was a one-line wrapper around structuredClone(), which is universally available in Node 17+ and all modern browsers. Removing the abstraction reduces indirection and means contributors don't need to learn a project-specific name for a well-known built-in. - Remove deepClone from packages/utils/src/object.ts and index.ts - Replace all 17 call sites with structuredClone() directly - Update check:utils script suggestion text - Update CLAUDE.md and global.md docs * fix(utils): add missing biome noRestrictedImports rule and correct truncate docs - Add noRestrictedImports to biome.json under style — bans nanoid and uuid package imports at lint time (crypto.randomUUID/randomBytes are caught by the check:utils grep script which handles global property access) - Correct truncate() TSDoc and parameter name: sliceLength makes it clear that total output length is sliceLength + suffix.length, matching the behavior all callers were already written to expect * fix(utils): add missing getErrorMessage imports at 4 call sites The sweep agents added getErrorMessage calls without the corresponding import in 4 files, causing test failures. Added the missing imports. * fix(utils): fix build errors from getErrorMessage sweep and retry.ts Turbopack issue - Fix retry.ts cross-file import: Turbopack cannot resolve './random.js' for internal package imports; inline the jitter crypto call directly - Add missing getErrorMessage imports to 32 files where the sweep added calls without the corresponding import (caught by type-check and test runs) - Remove accidental getErrorMessage import from crowdstrike/query/route.ts which has its own domain-specific getErrorMessage for parsing CrowdStrike's JSON error format - Fix use-sub-block-value.ts type error from structuredClone narrowing: add 'as T' cast at emitValue callsite (safe — valueCopy is always a structural copy of newValue) * fix(tools): use toError in crowdstrike catch block instead of local getErrorMessage The catch block was calling the local getErrorMessage function which parses CrowdStrike API JSON responses, not JavaScript Error objects. Use toError(error).message to correctly extract the message from a caught value in this context. |
||
|
|
c9118e775b |
feat(files): folders, multiselect, vfs update (#4572)
* v0.6.29: login improvements, posthog telemetry (#4026) * feat(posthog): Add tracking on mothership abort (#4023) Co-authored-by: Theodore Li <theo@sim.ai> * fix(login): fix captcha headers for manual login (#4025) * fix(signup): fix turnstile key loading * fix(login): fix captcha header passing * Catch user already exists, remove login form captcha * feat(files): folders + vfs update * address comments * address comments * cleanup unnused code * address comments * perf improvements * address next set * cycle detect * error handling * path improvements * cleanup, best practices * react query best practices: targeted invalidation, optimistic updates, key factory hierarchy - Add workspaceLists(workspaceId) intermediate key level to both workspaceFilesKeys and workspaceFileFolderKeys so invalidation targets only the affected workspace instead of all workspaces - Replace all lists() invalidation calls with workspaceLists(workspaceId) across every mutation (upload, rename, delete, restore, update content, folder mutations) - Add optimistic updates to useRenameWorkspaceFile and useUpdateWorkspaceFileFolder with onMutate snapshot, onError rollback, onSettled reconciliation - Move storage key into the content() factory as optional param so query keys are always built through the factory (useWorkspaceFileContent, useWorkspaceFileBinary) - Fix AnimatePresence wrapping in FilesActionBar so exit animation fires on deselect - Fix ResourceColGroup to use percentage weights instead of pixel widths to prevent horizontal scroll on narrow viewports * add shift-click range selection and selection-aware context menu for files - Extend SelectableConfig.onSelectRow with optional shiftKey param; DataRow captures shiftKey before onCheckedChange fires via a ref so the Radix Checkbox interaction chain stays intact - Implement shift-click range selection in files.tsx using lastSelectedIndexRef; tracks last-selected index in visibleRowIds to compute the range - Reset lastSelectedIndexRef on deselect and select-all - Add selectedCount prop to FileRowContextMenu; hide Open and Rename when multiple items are selected, show "Delete N items" / "Download N items" labels in multi-select mode * add Move submenu to file context menu and fix shift-click anchor update - Add nested Move submenu to FileRowContextMenu using DropdownMenuSub/SubTrigger/SubContent; shows available folders filtered by selection, converts '__root__' -> null for moving to the root level - Add handleContextMenuMove in files.tsx that calls moveItems.mutateAsync directly (no modal) and clears selection on success - Fix shift-click range selection: update lastSelectedIndexRef after range select so chained shift-clicks extend from the new anchor point correctly * fix move submenu: use folder names with tree-ordered indentation instead of stale paths - Compute folder depth from parentId chain client-side (avoids stale server-computed path field) - Tree-order folders so parents appear before their children, sorted by sortOrder then name - Show folder.name instead of folder.path so optimistic renames are reflected immediately - Indent each folder by depth * 12px in the submenu so po/shit renders as 'shit' indented under 'po' - MoveOption gains optional depth field; contextMenuMoveOptions is a separate memo from moveFolderOptions (modal keeps its existing path-label behavior) * fix shift-click anchor drift and remove dead stopPropagation constant - Remove dead stopPropagation const in resource.tsx (replaced by handleSelectRowClick) - Reset lastSelectedIndexRef when visibleRowIds changes so search/filter/folder navigation doesn't leave a stale anchor that produces wrong ranges on the next shift-click - Update lastSelectedIndexRef in handleRowContextMenu when right-clicking resets selection to a single item, so the anchor matches the newly-selected row - Add visibleRowIds to handleRowContextMenu deps (now reads it to compute anchor index) - Remove moveItems.mutateAsync from handleContextMenuMove deps per project convention (.mutateAsync is stable in TanStack v5) * complete workspace files feature: audit logs, posthog events, folder restore, empty state, keyboard shortcuts, storage indicator, breadcrumb rename - Audit + PostHog: wire file_renamed, file_deleted, file_moved, file_bulk_deleted, folder_created, folder_renamed, folder_deleted, folder_moved events to all file/folder API routes - Add AuditAction.FOLDER_UPDATED, FILE_MOVED, FOLDER_MOVED to audit types - Folder restore: server function, contract, API route (POST /files/folders/[folderId]/restore), hook (useRestoreWorkspaceFileFolder), Recently Deleted integration with new File Folders tab - Empty state: contextual emptyMessage passed to <Resource> based on search/filters/folder context - Keyboard shortcuts: Delete/Backspace deletes selection, Escape deselects, Cmd+A selects all (list view only, input-aware guard) - Storage indicator: useStorageInfo drives compact "used / limit" display in file list header via leadingActions - Breadcrumb rename: current folder breadcrumb gains Rename dropdown + inline editing via breadcrumbRename (useInlineRename) - Resource: thread leadingActions prop from ResourceProps to ResourceHeader * cleanup: accessibility, emcn design tokens, react best practices across workspace UI - Add sr-only ModalDescription to dialogs/modals for accessibility - Replace hardcoded colors and z-indices with design token CSS variables - Apply emcn design review fixes across tables, knowledge, logs, settings, workflows * fix audit and posthog: FOLDER_RESTORED action on restore, fire folder_moved event separately from file_moved * sidebar: add Files section with nested folder tree; polish move UX and cleanup - Files section in sidebar shows folder/file tree with expand/collapse, matching Workflows section structure; collapsed sidebar shows flyout menu - Move action bar now uses nested DropdownMenuSub tree instead of flat modal - Context menu and action bar share renderMoveOption from move-options.tsx - FolderInput added to emcn icons barrel; all FolderInput imports migrated - Drag ghost uses CSS vars (--border, --shadow-medium, --z-toast) - Selection pruning converted from useEffect to render-time comparison - Keyboard listener stabilized with handleBulkDeleteRef pattern - toError() used consistently in restore and move route handlers * remove Files section from sidebar * restore Files nav item in sidebar workspace section * fix infinite re-render on files page - revert selection pruning to useEffect * add filefolder resource type for ingesting workspace file folders * export filefolder tree types; add toast feedback for file/folder mutations * regenerate migration as 0208 after rebase onto staging * add workspaceFileFolder to schema mock * add FILE_MOVED, FOLDER_MOVED, FOLDER_UPDATED to audit mock * add filefolder ChatContext kind and wire through schema and resolver * add filefolder to AgentContextType * add filefolder to chat context kind registry; fix resolver to use workspaceFiles table * add .deepsec to gitignore * cleanup: effect, emcn tokens, mutation error handling - Replace selection-pruning useEffect with inline state adjustment during render - Fix drag overlay using invalid --accent HSL token → --brand-secondary; z-50 → z-[var(--z-dropdown)] - Move static inline styles on context menu trigger div to className - Add missing onError toast to useUpdateWorkspaceFileFolder, useRestoreWorkspaceFileFolder, useRestoreWorkspaceFile * lint * fix: remove duplicate handleCopilotStopGeneration from rebase * feat(copilot): folder-aware file context in WORKSPACE.md * feat(copilot): add move operation to file manage API * fix(files): make targetFolder optional in move file contract * perf(files): parallelize buffer fetches, fix N+1 folder queries, stabilize drag useMemo - download route: fan out all fetchWorkspaceFileBuffer calls with Promise.all before zip assembly so 100 files resolve in one round-trip instead of sequentially - getWorkspaceFileFolder: replace per-ancestor SELECTs with a single workspace-wide folder load + buildWorkspaceFileFolderPathMap, making depth irrelevant to query count - ensureWorkspaceFileFolderPath: pre-load all workspace folders in one SELECT before the segment loop; resolve existing segments from an in-memory map; only hit the DB to CREATE missing segments; conflict retry path preserved and also updates the map - files.tsx rowDragDropConfig: move activeDropTargetId into a ref so the useMemo does not recompute on every drag-over event * fix(files): remove files/ path stripping, fix stale path in optimistic update - splitWorkspaceFilePath: remove the unconditional .replace(/^files\//, '') that clobbered paths for files inside a folder literally named "files" - useUpdateWorkspaceFileFolder: when a name update is in flight, recompute the path field for the renamed folder (replace last segment) and propagate the new prefix to all descendant folders so breadcrumbs stay correct during the optimistic window * fix(files): revert broken ref opt, clean 409 on restore, null parentId on orphaned restore - files.tsx: revert the activeDropTargetId ref optimization — the ref doesn't trigger re-renders so the drop-target highlight never updated during drag; activeDropTargetId is back in state and in the rowDragDropConfig deps - restore/route.ts: catch Postgres 23505 unique-constraint violation and return a clean 409 instead of leaking the raw error as 400 - restoreWorkspaceFileFolder: check if the parent folder is still archived before restoring; if it is, restore to root (parentId: null) so the folder is never orphaned under an archived parent * feat(search): show folder path for files in cmd-k modal, strip extraneous comments - FileItem interface with folderPath?: string[] added to search modal utils - MemoizedFileItem component renders folder breadcrumb identically to MemoizedWorkflowItem — truncated path segments on the right with / separators - FilesGroup rewritten as a dedicated memo component (was createIconGroup factory) so it accepts FileItem[] and includes folderPath segments in the search value - searchModalFiles in sidebar splits f.folderPath string into string[] segments - search-modal.tsx typed to FileItem and includes folderPath in filterAndSort - Remove self-explanatory "Phase 1" section label from download route - Remove redundant TSDoc on the unique index in db schema * fix(workspace-files): audit fixes — transaction, status codes, contract refinements, guards * fix(vfs): pass folderPath separately so buildWorkspaceMd groups files correctly * fix(types): narrow unknown fileInput with Record cast after object guard * fix(routes): replace instanceof Error with toError() across new workspace file routes * improvement(files): cleanup pass — remove unnecessary useCallbacks, consolidate emcn icon imports - Remove useCallback from 5 drag-event handlers in DataRow (passed to native <tr> elements, no observer) - Remove stable useCallback fns from 3 useMemo deps arrays in files.tsx (editingId/editValue remain) - Merge all @/components/emcn/icons subpath imports into barrel (files.tsx, action-bar, file-row-context-menu) * fix(files): apply activeSort to folders, reject drop onto current parent folder - visibleFolders now respects activeSort column (name/updated/created) and direction so folder ordering stays consistent with file ordering - isInvalidDropTarget now returns true when all dragged items are already direct children of the target folder, preventing a no-op move mutation * fix breadcrumb * add new tools to rename, create, delete folders * move more ui actions into orchestration dir * address comments * fix params * fix tests * address comments * improve error codes * address comments * address more nits * fix mcp server error code --------- Co-authored-by: Theodore Li <theodoreqili@gmail.com> Co-authored-by: waleed <walif6@gmail.com> |
||
|
|
d721dc3358 |
feat(enterprise): add data drains for continuous export to S3 / webhook (#4440)
* feat(enterprise): add data drains for continuous export to S3 / webhook * chore(data-drains): regenerate migration on top of staging + bump route baseline Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * docs(data-drains): clarify retention pairing is user-coupled, not enforced Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(data-drains): preserve explicit forcePathStyle=false + reserve x-sim-signature Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * test(data-drains): drift guard ensures every webhook header is reserved Asserts that any header buildHeaders writes is rejected when reused as a custom signatureHeader. Adding a new metadata header without mirroring it into RESERVED_SIGNATURE_HEADER_NAMES now fails CI. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
68f66ba3f3 |
fix(credentials): clear stored refs on credential delete to prevent silent cascade orphaning (#4418)
* fix(credentials): clear stored refs on credential delete to prevent silent cascade orphaning * fix(testing): sync auditMock with new CREDENTIAL_RECONNECTED action * improvement(credentials): parallelize independent ref-clearing scans |
||
|
|
af55bad491 |
fix(uploads): direct-to-upload workspace files + shared transport (#4407)
* fix(uploads): direct-to-S3 workspace files + shared transport
* chore(testing): centralize posthog and storage-service mocks
* fix(uploads): address PR review — abort propagation, orphan cleanup, error handling
- Throw immediately on AbortError in KB retry loop (no useless 14s backoff)
- Cleanup S3/Blob object on quota or size-cap rejection in registerUploadedWorkspaceFile
- Enforce MAX_WORKSPACE_FILE_SIZE at registration (defense vs presigned PUT lying about size)
- Handle non-OK / non-JSON responses in workspace-files upload paths
* fix(uploads): add Zod contracts for workspace presigned/register routes
* fix(uploads): correct BlobServiceClient type name in headBlobObject
* fix(uploads): address PR review — typo, complete-failure cleanup, double-increment
* fix(uploads): preserve fallback size and reuse existing display name on re-register
* fix(uploads): surface server error message and bypass quota for local-storage fallback
* fix(uploads): align register response schema with UserFile; skip presigned for KB large files
- registerWorkspaceFileResponseSchema now matches the UserFile shape the route actually returns; previous schema required workspace DB-row fields that were never populated, causing requestJson validation to reject successful uploads.
- KB batch presigned fetch now skips files >= LARGE_FILE_THRESHOLD since multipart bypasses the per-file presigned URL anyway.
* fix(uploads): idempotent register skips duplicate audit/posthog; add edge-case tests
- registerUploadedWorkspaceFile now returns { file, created } so the route can skip captureServerEvent and recordAudit on idempotent re-register (existing metadata reused). Previously a re-register fired duplicate analytics + audit log entries.
- Add tests covering: idempotent re-register skips audit/analytics, isNetworkError matches econnreset/timeout/etc keywords, multipart complete failure fires action=abort cleanup.
* fix(uploads): include 50MiB boundary in batch presigned fetch
* fix(uploads): trust HEAD size to prevent quota inflation
The head.size > 0 fallback let a client PUT 0 bytes and register
with an inflated size, debiting quota without storing data. HEAD
on an existing object always returns the true byte count, so trust
it directly — a genuine 0-byte file correctly contributes 0.
* fix(uploads): audit verified file size, not client-supplied
* fix(uploads): handle register retries and name-collision races
Two bugs in registerUploadedWorkspaceFile:
1. Register retry could orphan storage. When a successful response
was lost on the wire and the client retried, the quota check saw
the bytes already counted, failed, and cleanupOrphan deleted the
already-registered storage object — leaving the DB row pointing
to nothing. Fix: check getFileMetadataByKey before quota guard
and short-circuit on existing record.
2. Concurrent same-named uploads could lose data. allocateUniqueWorkspaceFileName
is best-effort; two racing uploads can pass it and both attempt
the same display name. The loser's insert hits 23505, the catch
block called cleanupOrphan, and successfully-uploaded bytes
were deleted. Fix: retry on 23505 with a fresh allocateUniqueWorkspaceFileName,
matching the pattern in uploadWorkspaceFile. Throw FileConflictError
after exhaustion.
* fix(uploads): retry transient DirectUploadErrors at outer KB level
The KB outer retry only triggered on isNetworkError, missing
transient 5xx from S3/Azure (DirectUploadError code
DIRECT_UPLOAD_ERROR or MULTIPART_ERROR). Adds isTransientUploadError
and retries on it, restoring resilience for small-file presigned
PUTs against flaky cloud storage.
* fix(uploads): only retry transient 5xx, not deterministic 4xx
DirectUploadError now carries the HTTP status. isTransientUploadError
gates on 5xx so callers don't loop on 400/403/404 (e.g., malformed
request, expired signature). Multipart per-part retry also short-circuits
on 4xx — same reasoning.
* refactor(uploads): collapse getFileContentType into resolveFileType
The two helpers differed only in whether application/octet-stream
falls back to the extension map. Add an option flag to resolveFileType
and keep getFileContentType as a thin wrapper for direct-PUT callers
that need to preserve the exact browser-reported content-type.
* chore(uploads): trim verbose comments
Drop inline comments that restate code ("Use the full storageKey as fileName"),
collapse a multi-line block comment into a tighter TSDoc on the existence
check, and prune verbose vitest file headers — describe blocks already
document what's tested.
* fix(uploads): regenerate fileId per insert retry; require cloud storage for register
* fix(uploads): cap formdata fallback at 100MB; drop unused size param
* fix(uploads): abort multipart on get-part-urls failure; retry register on transient errors
* fix(uploads): drop vestigial size field from register contract
* fix(uploads): abort multipart on complete-fetch throw
* fix(uploads): set kb presignedEndpoint fallback; race-safe blob HEAD
* fix(uploads): include ?type=knowledge-base on kb presigned fallback
* fix(uploads): remove abort listener on xhr timeout
* fix(uploads): add timeout/abort to kb api fallback upload
|
||
|
|
879dab9f19 |
feat(table): make plan table limits configurable via env vars (#4406)
* feat(table): make plan table limits configurable via env vars * fix(table): coerce env table limits to number for skipValidation env * improvement(env): extract envNumber helper for numeric env coercion * improvement(knowledge): use envNumber helper for KB_CONFIG_* env reads * fix(testing): add envNumber to env mock factory * fix(env): allow zero in envNumber for max-throughput configs * fix(env): add min option to envNumber for strict-positive configs |
||
|
|
af859cd508 |
feat(workflows): lock/duplicate improvements for workflows (#4387)
* feat(workflows): lock/duplicate improvements * fix duplicate var remap bug * address comments * remove dead vars * fix tests * address comments * code cleanup * address comments * address comments * minor change * remove dead code |
||
|
|
e2b3ae43e3 |
fix(terminal): correct error/cancel block status in logs panel (#4372)
* fix(terminal): correct error/cancel block status in logs panel Three bugs in the workflow editor's terminal/logs panel where block status diverged from the engine's truth on error paths: 1. **Errored block shown as "canceled"** — when the SSE 'add' mode produced a duplicate entry on block error and `cancelRunningEntries` then swept the original placeholder. 2. **Upstream blocks stuck on "Running"** — terminal events arrived before the engine's last block events under reconnect/timeout, so the live panel never received the per-block terminal state. 3. **Phantom "Run Error" pseudo-row** — the failing block rendered as "canceled" while a synthetic row carried the real error text. Fixes: - **Fix B** (`addConsoleErrorEntry`): when a running placeholder exists for `(blockId, executionId)`, route through `updateConsoleErrorEntry` instead of creating a second entry. Aligns 'add' mode with the existing 'update' mode behavior. - **Fix C** (`reconcileFinalBlockLogs`): terminal SSE events now carry `finalBlockLogs` (server-authoritative snapshot). On execution:error / execution:cancelled, reconcile any still-running entries with their server-side terminal state. Recovers correctness on network drop, server timeout/abort, and reconnect-resume paths where individual block:* events may not have reached the client. - **Fix D** (`addExecutionErrorConsoleEntry`): cross-check `useTerminalConsoleStore` for entries with `error` set scoped to the executionId before emitting the synthetic "Run Error" row. Suppresses the phantom row when the failing block already carries the message. - **Signature refactor**: `handleExecutionErrorConsole` / `handleExecutionCancelledConsole` now take a typed `ExecutionConsoleDeps` object instead of stacking positional deps — matches the existing `createBlockEventHandlers(config, deps)` precedent in the same file. Tests: - 12 tests in `workflow-execution-utils.test.ts` covering Fix B/C/D and the deps-object signature refactor. - Centralized terminal-console store mock in `@sim/testing` so future tests can stub the store without per-file boilerplate. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(terminal): wire copilot cancellation to finalBlockLogs reconciliation Address Greptile review: - `executeWorkflowWithFullLogging`'s `onExecutionCancelled` was `() => {}` and silently dropped the `finalBlockLogs` payload, so Bug 2's "upstream blocks stuck on Running" fix did not fire on copilot-initiated cancellations. Wire it through `handleExecutionCancelledConsole` to match the SSE-route `onExecutionCancelled` path. - Test for the `blockType !== 'error'` filter used a different `executionId` than the seeded entry, so the executionId scope rejected the entry before the blockType predicate ran. Align executionIds so the test actually exercises the filter. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(terminal): pass durationMs in reconnect cancellation handler Reconnect-resume `onExecutionCancelled` was forwarding `finalBlockLogs` but not `data?.duration`, so the "Run Cancelled" entry rendered with a 0ms duration. Match the other two `handleExecutionCancelledConsole` callsites. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
0c25fc4ee1 |
fix(auth): resolve CORS errors for self-hosted deployments behind reverse proxies (#4369)
* fix(auth): resolve CORS errors for self-hosted deployments behind reverse proxies
- auth client now uses browser origin first, falling back to NEXT_PUBLIC_APP_URL
- socket client falls back to page origin when served from non-localhost (assumes /socket.io is proxied)
- add TRUSTED_ORIGINS env var to extend Better Auth trustedOrigins (apex+www, alias hostnames)
- warn at startup when NEXT_PUBLIC_APP_URL is localhost in production
- preprocess empty NEXT_PUBLIC_SOCKET_URL so docker-compose ${VAR:-} works
- migrate remaining uuid/nanoid/randomUUID usages to @sim/utils generateId/generateShortId
- extend generateShortId with optional alphabet param (rejection sampling)
- document TRUSTED_ORIGINS in .env.example, docker-compose.prod.yml, and helm values.yaml
Fixes simstudioai/sim#1243
* fix(auth): address PR review comments
* chore(env): drop unnecessary NEXT_PUBLIC_SOCKET_URL preprocess (skipValidation is true)
* fix(docker): include @sim/utils in migrations image
Migration scripts now import generateId from @sim/utils/id; without copying packages/utils into the image, bun install fails to resolve the workspace dep at build time and the import fails at runtime.
* fix(helm): remove unused NEXT_PUBLIC_SOCKET_URL from realtime sections
The realtime service never reads NEXT_PUBLIC_SOCKET_URL — its env schema
only includes BETTER_AUTH_URL, NEXT_PUBLIC_APP_URL, ALLOWED_ORIGINS,
BETTER_AUTH_SECRET, INTERNAL_API_SECRET, DATABASE_URL, and REDIS_URL.
Remove the dead config from all helm values files and the values schema.
* fix(helm): allow empty NEXT_PUBLIC_SOCKET_URL in values schema
The default in values.yaml is now "" (empty string), which falls back to
the page origin at runtime. The schema previously required a valid URI,
which would reject the default. Mirror the INTERNAL_API_BASE_URL pattern
using anyOf with const "". Also add TRUSTED_ORIGINS to the schema.
* docs(self-hosting): mark NEXT_PUBLIC_SOCKET_URL as optional
The page-origin fallback in getSocketUrl() means self-hosters no longer
need to set NEXT_PUBLIC_SOCKET_URL when realtime is on the same origin
as the app. Update docs to reflect this:
- Remove NEXT_PUBLIC_SOCKET_URL from .env scaffolding examples in
docker.mdx, platforms.mdx, environment-variables.mdx
- Mark the variable as Optional in the env vars table with the new
default behavior described
- Update troubleshooting to point at reverse-proxy /socket.io routing
rather than the env var
- Flip dev docker-compose defaults (local, ollama, devcontainer) from
http://localhost:3002 to empty for consistency with prod.yml; the
in-code localhost fallback handles the dev case identically
Applied across all 6 documentation languages (en/fr/de/ja/es/zh).
* chore: untrack and ignore .claude/scheduled_tasks.lock
|
||
|
|
b8959eb20d |
improvement(repo): zod based client-server boundary (#4355)
* improvement(repo): centralized zod contracts (#4336) * improvement(repo): zod schema contracts * type checks * fix(notion): correctly register tool (#4337) * fix func blokc * more improvements * fix tests * type check * remove v3 refs * minor type improvements * address comments * update jira contract * remove validateJsonBody * improvement(repo): consolidation of boundary helpers + better unknown usage (#4352) * improvement(repo): consolidation of boundary helpers + better unknown usage * address comments * improve file transfer error messaging * fix docs listing schema drift * fix inocrrect type casting * address council comments * remove prefix |
||
|
|
36742740aa |
fix(cleanup): batch orphaned snapshot deletes to avoid slow-query spike (#4348)
* fix(cleanup): batch orphaned snapshot deletes to avoid slow-query spike * fix(cleanup): recheck orphan condition in delete to close TOCTOU gap |
||
|
|
2e3de9ac8a |
feat(governance): external workspace users from outside org (#4313)
* feat(governance): external workspace users from outside org * update docs * address comments * edge case improvements * remove unused fallback * address comments * add outbox for seat reduction * fix edge case with org join after invite * add server side batch invites for workspace * use zod schema for route |
||
|
|
04f1d015f3 | fix(mothership): Use heartbeat mechanism for chat locks (#4286) | ||
|
|
5f0f0edd63 |
improvement(repo): separate realtime into separate app (#4262)
* improvement(repo): restructuring to make realtime image narrower scoped * improvements * chore(repo): rebase fixes and quality improvements for realtime split Addresses merge-time issues and gaps from the realtime app split: - Retarget stale vi.mock paths to @sim/workflow-persistence/subblocks - Restore README branding, fix AGENTS.md script reference - Restore TSDoc on workflow-persistence subblocks helpers - Use toError() from @sim/utils/errors in save.ts - Add vitest config + local mocks so @sim/audit tests run standalone - Move socket.io-client to devDependencies in apps/realtime - Add missing package COPY steps to docker/app.Dockerfile - Add check:boundaries/check:realtime-prune scripts and wire into CI Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * refactor(security): consolidate crypto primitives into @sim/security Move general-purpose crypto primitives out of apps/sim into the @sim/security package so both apps/sim and apps/realtime can share them. @sim/security exports (all pure, dependency-free): ./compare safeCompare (constant-time HMAC-wrapped equality) ./encryption encrypt/decrypt (AES-256-GCM, iv:cipher:tag format) ./hash sha256Hex ./tokens generateSecureToken (base64url) Migrate apps/sim call sites to use these + @sim/utils helpers: crypto.randomUUID() -> generateId() from @sim/utils/id createHash('sha256').digest -> sha256Hex timingSafeEqual on hashed hex -> safeCompare new Promise(setTimeout) -> sleep from @sim/utils/helpers No behavior change: encryption format, digest output, and token length are preserved exactly. * refactor(copilot): use toError in remaining otel/finalize sites Replace the last two `error instanceof Error ? error : new Error(String(error))` patterns with toError from @sim/utils/errors. Completes the sweep of clean candidates — no behavior change. * refactor(security): consolidate HMAC-SHA256 primitives into @sim/security Adds hmacSha256Hex and hmacSha256Base64 to @sim/security/hmac and migrates 15 webhook providers plus 5 other hot paths (deployment token signing, outbound webhook requests, workspace notification delivery, notification test route, Shopify OAuth callback) off bare `createHmac` calls. Secret parameter accepts `string | Buffer` to cover base64-decoded Svix-style secrets (Resend) and MS Teams' HMAC scheme. AWS SigV4 signing in S3 and Textract tools intentionally retains direct `createHmac` usage — its multi-step key derivation chain doesn't fit a generic helper. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * chore(packages): post-audit test + packaging polish - Add safeCompare unit tests (identity, length mismatch, hex-nibble diff). - Add Buffer-secret cases to hmac tests to lock in Svix/MS-Teams contract. - Declare `reactflow` as a peerDependency on @sim/workflow-types — only used for type imports. - Add a barrel export to @sim/workflow-persistence for consumers that prefer package-level imports; subpath exports retained. - Document the data-field invariant in load.ts for loop/parallel subflow patching. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * chore(realtime): address PR review feedback - Remove redundant SOCKET_PORT=3002 env from Dockerfile runner stage (env.PORT already defaults to 3002 via zod schema). - Reorder PORT fallback so an explicitly-set SOCKET_PORT wins over the schema default for PORT; keeps SOCKET_PORT functional as an override instead of dead code. - Add dedicated type-check CI step for @sim/realtime so TS errors surface pre-deploy (the Dockerfile runs source TS via Bun and has no implicit build-time type check). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * chore(realtime): remove unused SOCKET_PORT env var SOCKET_PORT has lived in the socket server since the June 2025 refactor but was never actually set in any deploy config — docker-compose.prod, helm values/templates, .env.example, and docs all use PORT or the 3002 default exclusively. No self-hoster was ever pointed at SOCKET_PORT, so removing it is safe. Simplifies realtime port resolution to `env.PORT` (zod-validated with a 3002 default) and drops the orphaned sim-side schema entry. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Waleed Latif <walif6@gmail.com> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
8ce56fe1f2 |
fix(auth): add api key auth via sha256 hash lookup (#4266)
* fix(auth): add api key auth via sha256 hash lookup * Remove promise all logic * Restore feature flag * fix feature flag * Combine auth and hash gate |
||
|
|
699bbfd16f |
feat(log): Add wrapper function for standardized logging (#4061)
* feat(log): Add wrapper function for standardized logging * Add all routes to wrapper, handle background execution * fix lint * fix test * fix test missing url * fix lint * fix tests * fix build * fix(build): unmangle generic in admin outbox requeue route Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
0e1ff0a1ac |
improvement(enterprise): slack wizard UI, enterprise docs, data retention updates (#4241)
* improvement(enterprise): slack wizard UI, enterprise docs, data retention updates * improvement(docs): add enterprise screenshots to sso, access-control, whitelabeling pages * form * fix(enterprise): address PR review — h-full for recently-deleted, shared SettingRow, toast UX, stale form fix, emcn tokens * fix(whitelabeling): scope drop zone to thumbnail only, not full upload row * fix(whitelabeling): remove drop image text from drag overlay * fix(config): add DATA_RETENTION_ENABLED to env schema to fix build type error * fix(testing): add isDataRetentionEnabled to feature flags mock * improvement(docs): remove redundant requirements section from data-retention page * improvement(docs): remove requirements sections from all enterprise doc pages * improvement(docs): add screenshot to audit-logs page * fix(data-retention): bypass enterprise gate when billing is disabled for self-hosted |
||
|
|
ac4ccfcac8 |
fix(billing): close TOCTOU race in subscription transfer, centralize stripe test mocks (#4239)
* fix(billing): close TOCTOU race in subscription transfer, centralize stripe test mocks
* more mocks
* fix(testing): provide complete Stripe.Event defaults in createMockStripeEvent
* fix(testing): make dbChainMock .for('update') chainable with .limit()
* fix(billing): gate subscription transfer noop behind membership check
Previously the 'already belongs to this organization' early return fired
before the org/member lookups, letting any authenticated caller probe
sub-to-org pairings without being a member of the target org. Move the
noop check after the admin/owner verification so unauthorized callers
hit the 403 first.
|
||
|
|
d9209f9588 |
improvement(governance): workspace-org invitation system consolidation (#4230)
* workspace re-org checkpoint * admin route reconciliation * checkpoint consistency fixes * prep merge * regen migration * checkpoint * code cleanup * update docs * add feature for owner to leave + admin route * address comments * fix new account race * address comments |
||
|
|
5cf7e8d546 |
improvement(codebase): migrate tests to dbChainMock, extract react-query hooks (#4235)
* improvement(codebase): migrate tests to dbChainMock, extract react-query hooks Migrate 97 test files to centralized dbChainMock/dbChainMockFns helpers from @sim/testing — removes hoisted chain-wiring boilerplate. Extend dbChainMock to cover insert/update/delete/transaction/execute patterns. Extract useGitHubStars and useVoiceSettings react-query hooks from inline fetches. Centralize additional mocks (authMockFns, hybridAuthMockFns) and update docs. * fix(github-stars): centralize fallback via initialData, remove stale constants Move the placeholder star count into useGitHubStars as initialData with initialDataUpdatedAt: 0 so `data` is always a narrowed string while still refetching on mount. Fixes two Bugbot issues: stale '25.8k' in chat.tsx (vs '27.8k' in navbar) and empty-string return in fetchGitHubStars that bypassed `??` fallbacks in consumers. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(testing): wire dbChainMock.db to shared transaction and execute fns dbChainMock.db.transaction was an inline vi.fn() separate from the exported dbChainMockFns.transaction, so dbChainMockFns.transaction.mockResolvedValueOnce and assertions silently targeted the wrong instance. dbChainMock.db also omitted execute, so tests for any module that calls db.execute (logging-session, table service, billing balance) would throw TypeError. Both mocks now reference the module-level constants so overrides and resetDbChainMock affect the same fn. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(chat,testing): memoize welcome message and add selectDistinct to dbChainMock.db Why: - Welcome ChatMessage was rebuilt inline each render, producing a fresh timestamp and new array identity — cascading to ChatMessageContainer and VoiceInterface props on every tick. - dbChainMockFns exports selectDistinct/selectDistinctOn but the dbChainMock.db object omitted them, so tests that stub those builders hit undefined on the mocked module. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(chat): re-attach scroll listener once container mounts The scroll effect's empty dep array meant it ran only on the first render, when `chatConfig` is still loading and the component returns `<ChatLoadingState />` — so `messagesContainerRef.current` was null and the listener was never attached. Depend on the gating conditions that control which tree renders, so the effect re-runs once the real container is in the DOM (and re-attaches when toggling in/out of voice mode). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(chat): reset chat state on identifier change via key prop Keying `<ChatClient>` on `identifier` guarantees a full remount on route transitions between chats, so `conversationId`, `messages`, and every other piece of local state start fresh — no reset effect required. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
b5674d9ed4 |
improvement(codebase): centralize test mocks, extract @sim/utils, remove dead code (#4228)
* improvement(codebase): centralize test mocks, extract @sim/utils, remove dead code * improvement(codebase): apply @sim/utils conventions to staging-introduced files |
||
|
|
0abcc6e813 |
improvement(mothership): restructured stream, tool structures, code typing, file write/patch/append tools, timing issues (#4090)
* fix build error * improvement(mothership): new agent loop (#3920) * feat(transport): replace shared chat transport with mothership-stream module * improvement(contracts): regenerate contracts from go * feat(tools): add tool catalog codegen from go tool contracts * feat(tools): add tool-executor dispatch framework for sim side tool routing * feat(orchestrator): rewrite tool dispatch with catalog-driven executor and simplified resume loop * feat(orchestrator): checkpoint resume flow * refactor(copilot): consolidate orchestrator into request/ layer * refactor(mothership): reorganize lib/copilot into structured subdirectories * refactor(mothership): canonical transcript layer, dead code cleanup, type consolidation * refactor(mothership): rebase onto latest staging * refactor(mothership): rename request continue to lifecycle * feat(trace): add initial version of request traces * improvement(stream): batch stream from redis * fix(resume): fix the resume checkpoint * fix(resume): fix resume client tool * fix(subagents): subagent resume should join on existing subagent text block * improvement(reconnect): harden reconnect logic * fix(superagent): fix superagent integration tools * improvement(stream): improve stream perf * Rebase with origin dev * fix(tests): fix failing test * fix(build): fix type errors * fix(build): fix build errors * fix(build): fix type errors * feat(mothership): add cli execution * fix(mothership): fix function execute tests * Force redeploy * feat(motheship): add docx support * feat(mothership): append * Add deps * improvement(mothership): docs * File types * Add client retry logic * Fix stream reconnect * Eager tool streaming * Fix client side tools * Security * Fix shell var injection * Remove auto injected tasks * Fix 10mb tool response limit * Fix trailing leak * Remove dead tools * file/folder tools * Folder tools * Hide function code inline * Dont show internal tool result reads * Fix spacing * Auth vfs * Empty folders should show in vfs * Fix run workflow * change to node runtime * revert back to bun runtime * Fix * Appends * Remove debug logs * Patch * Fix patch tool * Temp * Checkpoint * File writes * Fix * Remove tool truncation limits * Bad hook * replace react markdown with streamdown * Checkpoitn * fix code block * fix stream persistence * temp * Fix file tools * tool joining * cleanup subagent + streaming issues * streamed text change * Tool display intetns * Fix dev * Fix tests * Fix dev * Speed up dev ci * Add req id * Fix persistence * Tool call names * fix payload accesses * Fix name * fix snapshot crash bug * fix * Fix * remove worker code * Clickable resources * Options ordering * Folder vfs * Restore and mass delete tools * Fix * lint * Update request tracing and skills and handlers * Fix editable * fix type error * Html code * fix(chat): make inline code inherit parent font size in markdown headers Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * improved autolayout * durable stream for files * one more fix * POSSIBLE BREAKAGE: SCROLLING * Fixes * Fixes * Lint fix * fix(resource): fix resource view disappearing on ats (#4103) Co-authored-by: Theodore Li <theo@sim.ai> * Fixes * feat(mothership): add execution logs as a resource type Adds `log` as a first-class mothership resource type so copilot can open and display workflow execution logs as tabs alongside workflows, tables, files, and knowledge bases. - Add `log` to MothershipResourceType, all Zod enums, and VALID_RESOURCE_TYPES - Register log in RESOURCE_REGISTRY (Library icon) and RESOURCE_INVALIDATORS - Add EmbeddedLog and EmbeddedLogActions components in resource-content - Export WorkflowOutputSection from log-details for reuse in EmbeddedLog - Add log resolution branch in open_resource handler via new getLogById service - Include log id in get_workflow_logs response and extract resources from output - Exclude log from manual add-resource dropdown (enters via copilot tools only) - Regenerate copilot contracts after adding log to open_resource Go enum * Fix perf and message queueing * Fix abort * fix(ui): dont delete resource on clearing from context, set resource closed on new task (#4113) Co-authored-by: Theodore Li <theo@sim.ai> * improvement(mothership): structure sim side typing * address comments * reactive text editor tweaks * Fix file read and tool call name persistence bug * Fix code stream + create file opening resource * fix use chat race + headless trace issues * Fix type issue * Fix mothership block req lifecycle * Fix build * Move copy reqid * Fix * fix(ui): fix resource tag transition from home to task (#4132) Co-authored-by: Theodore Li <theo@sim.ai> * Fix persistence --------- Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai> Co-authored-by: Waleed Latif <walif6@gmail.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: Theodore Li <theo@sim.ai> Co-authored-by: Theodore Li <theodoreqili@gmail.com> |
||
|
|
30c5e82ab0 |
feat(ee): add enterprise audit logs settings page (#4111)
* feat(ee): add enterprise audit logs settings page with server-side search Add a new audit logs page under enterprise settings that displays all actions captured via recordAudit. Includes server-side search, resource type filtering, date range selection, and cursor-based pagination. - Add internal API route (app/api/audit-logs) with session auth - Extract shared query logic (buildFilterConditions, buildOrgScopeCondition, queryAuditLogs) into app/api/v1/audit-logs/query.ts - Refactor v1 and admin audit log routes to use shared query module - Add React Query hook with useInfiniteQuery and cursor pagination - Add audit logs UI with debounced search, combobox filters, expandable rows - Gate behind requiresHosted + requiresEnterprise navigation flags - Place all enterprise audit log code in ee/audit-logs/ Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * lint * fix(ee): fix build error and address PR review comments - Fix import path: @/lib/utils → @/lib/core/utils/cn - Guard against empty orgMemberIds array in buildOrgScopeCondition - Skip debounce effect on mount when search is already synced Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * lint * fix(ee): fix type error with unknown metadata in JSX expression Use ternary instead of && chain to prevent unknown type from being returned as ReactNode. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ee): align skeleton filter width with actual component layout Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * lint * feat(audit): add audit logging for passwords, credentials, and schedules - Add PASSWORD_RESET_REQUESTED audit on forget-password with user lookup - Add CREDENTIAL_CREATED/UPDATED/DELETED audit on credential CRUD routes with metadata (credentialType, providerId, updatedFields, envKey) - Add SCHEDULE_CREATED audit on schedule creation with cron/timezone metadata - Fix SCHEDULE_DELETED (was incorrectly using SCHEDULE_UPDATED for deletes) - Enhance existing schedule update/disable/reactivate audit with structured metadata (operation, updatedFields, sourceType, previousStatus) - Add CREDENTIAL resource type and Credential filter option to audit logs UI - Enhance password reset completed description with user email Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(audit): align metadata with established recordAudit patterns - Add actorName/actorEmail to all new credential and schedule audit calls to match the established pattern (e.g., api-keys, byok-keys, knowledge) - Add resourceId and resourceName to forget-password audit call - Enhance forget-password description with user email Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(testing): sync audit mock with new AuditAction and AuditResourceType entries Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor(audit-logs): derive resource type filter from AuditResourceType Instead of maintaining a separate hardcoded list, the filter dropdown now derives its options directly from the AuditResourceType const object. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(audit): enrich all recordAudit calls with structured metadata - Move resource type filter options to ee/audit-logs/constants.ts (derived from AuditResourceType, no separate list to maintain) - Remove export from internal cursor helpers in query.ts - Add 5 new AuditAction entries: BYOK_KEY_UPDATED, ENVIRONMENT_DELETED, INVITATION_RESENT, WORKSPACE_UPDATED, ORG_INVITATION_RESENT - Enrich ~80 recordAudit calls across the codebase with structured metadata (knowledge bases, connectors, documents, workspaces, members, invitations, workflows, deployments, templates, MCP servers, credential sets, organizations, permission groups, files, tables, notifications, copilot operations) - Sync audit mock with all new entries Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(audit): remove redundant metadata fields duplicating top-level audit fields Remove metadata entries that duplicate resourceName, workspaceId, or other top-level recordAudit fields. Also remove noisy fileNames arrays from bulk document upload audits (kept fileCount). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(audit): split audit types from server-only log module Extract AuditAction, AuditResourceType, and their types into lib/audit/types.ts (client-safe, no @sim/db dependency). The server-only recordAudit stays in log.ts and re-exports the types for backwards compatibility. constants.ts now imports from types.ts directly, breaking the postgres -> tls client bundle chain. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(audit): escape LIKE wildcards in audit log search query Escape %, _, and \ characters in the search parameter before embedding in the LIKE pattern to prevent unintended broad matches. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(audit): use actual deletedCount in bulk API key revoke description The description was using keys.length (requested count) instead of deletedCount (actual count), which could differ if some keys didn't exist. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(audit-logs): fix OAuth label displaying as "Oauth" in filter dropdown ACRONYMS set stored 'OAuth' but lookup used toUpperCase() producing 'OAUTH' which never matched. Now store all acronyms uppercase and use a display override map for special casing like OAuth. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
1189400167 |
feat(enterprise): cloud whitelabeling for enterprise orgs (#4047)
* feat(enterprise): cloud whitelabeling for enterprise orgs * fix(enterprise): scope enterprise plan check to target org in whitelabel PUT * fix(enterprise): use isOrganizationOnEnterprisePlan for org-scoped enterprise check * fix(enterprise): allow clearing whitelabel fields and guard against empty update result * fix(enterprise): remove webp from logo accept attribute to match upload hook validation * improvement(billing): use isBillingEnabled instead of isProd for plan gate bypasses * fix(enterprise): show whitelabeling nav item when billing is enabled on non-hosted environments * fix(enterprise): accept relative paths for logoUrl since upload API returns /api/files/serve/ paths * fix(whitelabeling): prevent logo flash on refresh by hiding logo while branding loads * fix(whitelabeling): wire hover color through CSS token on tertiary buttons * fix(whitelabeling): show sim logo by default, only replace when org logo loads * fix(whitelabeling): cache org logo url in localstorage to eliminate flash on repeat visits * feat(whitelabeling): add wordmark support with drag/drop upload * updated turbo * fix(whitelabeling): defer localstorage read to effect to prevent hydration mismatch * fix(whitelabeling): use layout effect for cache read to eliminate logo flash before paint * fix(whitelabeling): cache theme css to eliminate color flash before org settings resolve * fix(whitelabeling): deduplicate HEX_COLOR_REGEX into lib/branding and remove mutation from useCallback deps * fix(whitelabeling): use cookie-based SSR cache to eliminate brand flash on all page loads * fix(whitelabeling): use !orgSettings condition to fix SSR brand cache injection React Query returns isLoading: false with data: undefined during SSR, so the previous brandingLoading condition was always false on the server — initialCache was never injected into brandConfig. Changing to !orgSettings correctly applies the cookie cache both during SSR and while the client-side query loads, eliminating the logo flash on hard refresh. |
||
|
|
89ae738745 |
feat(folders): soft-delete folders and show in Recently Deleted (#4001)
* feat(folders): soft-delete folders and show in Recently Deleted Folders are now soft-deleted (archived) instead of permanently removed, matching the existing pattern for workflows, tables, and knowledge bases. Users can restore folders from Settings > Recently Deleted. - Add `archivedAt` column to `workflowFolder` schema with index - Change folder deletion to set `archivedAt` instead of hard-delete - Add folder restore endpoint (POST /api/folders/[id]/restore) - Batch-restore all workflows inside restored folders in one transaction - Add scope filter to GET /api/folders (active/archived) - Add Folders tab to Recently Deleted settings page - Update delete modal messaging for restorable items - Change "This action cannot be undone" styling to muted text Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(testing): add FOLDER_RESTORED to audit mock Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(folders): atomic restore transaction and scope to folder-deleted workflows Address two review findings: - Wrap entire folder restore in a single DB transaction to prevent partial state if any step fails - Only restore workflows archived within 5s of the folder's archivedAt, so individually-deleted workflows are not silently un-deleted - Add folder_restored to PostHog event map Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor(folders): simplify restore to remove hacky 5s time window The 5-second time window for scoping which workflows to restore was a fragile heuristic (magic number, race-prone, non-deterministic). Restoring a folder now restores all archived workflows in it, matching standard trash/recycle-bin behavior. Users can re-delete any workflow they don't want after restore. The single-transaction wrapping from the prior commit is kept — that was a legitimate atomicity fix. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(db): regenerate folder soft-delete migration with drizzle-kit Replace manually created migration with proper drizzle-kit generated one that includes the snapshot file, fixing CI schema sync check. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore(db): fix migration metadata formatting Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(folders): scope restore to folder-deleted workflows via shared timestamp Use a single timestamp across the entire folder deletion — folders, workflows, schedules, webhooks, etc. all get the exact same archivedAt. On restore, match workflows by exact archivedAt equality with the folder's timestamp, so individually-deleted workflows are not silently un-deleted. - Add optional archivedAt to ArchiveWorkflowOptions (backwards-compatible) - Pass shared timestamp through deleteFolderRecursively → archiveWorkflowsByIdsInWorkspace - Filter restore with eq(workflow.archivedAt, folderArchivedAt) instead of isNotNull Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(workflows): clear folderId on restore when folder is archived or missing When individually restoring a workflow from Recently Deleted, check if its folder still exists and is active. If the folder is archived or missing, clear folderId so the workflow appears at root instead of being orphaned (invisible in sidebar). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(folders): format restoreFolderRecursively call to satisfy biome Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(folders): close remaining restore edge cases Three issues caught by audit: 1. Child folder restore used isNotNull instead of timestamp matching, so individually-deleted child folders would be incorrectly restored. Now uses eq(archivedAt, folderArchivedAt) for both workflows AND child folders — consistent and deterministic. 2. No workspace archived check — could restore a folder into an archived workspace. Now checks getWorkspaceWithOwner, matching the existing restoreWorkflow pattern. 3. Re-restoring an already-restored folder returned an error. Now returns success with zero counts (idempotent). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(folders): add archivedAt to optimistic folder creation objects Ensures optimistic folder objects include archivedAt: null for consistency with the database schema shape. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(folders): handle missing parent folder during restore reparenting If the parent folder row no longer exists (not just archived), the restored folder now correctly gets reparented to root instead of retaining a dangling parentId reference. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
a680cec78f |
fix(core): consolidate ID generation to prevent HTTP self-hosted crashes (#3977)
* fix(core): consolidate ID generation to prevent HTTP self-hosted crashes crypto.randomUUID() requires a secure context (HTTPS) in browsers, causing white-screen crashes on self-hosted HTTP deployments. This replaces all direct usage of crypto.randomUUID(), nanoid, and the uuid package with a central utility that falls back to crypto.getRandomValues() which works in all contexts. - Add generateId(), generateShortId(), isValidUuid() in @/lib/core/utils/uuid - Replace crypto.randomUUID() imports across ~220 server + client files - Replace nanoid imports with generateShortId() - Replace uuid package validate with isValidUuid() - Remove nanoid dependency from apps/sim and packages/testing - Remove browser polyfill script from layout.tsx - Update test mocks to target @/lib/core/utils/uuid - Update CLAUDE.md, AGENTS.md, cursor rules, claude rules Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * update bunlock * fix(core): remove UUID_REGEX shim, use isValidUuid directly * fix(core): remove deprecated uuid mock helpers that use vi.doMock --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
0abeac77e1 |
improvement(platform): standardize perms, audit logging, lifecycle across admin, copilot, ui actions (#3858)
* improvement(platform): standardize perms, audit logging, lifecycle mgmt across admin, copilot, ui actions * address comments * improve error codes * address bugbot comments * fix test |
||
|
|
d3d58a9615 |
Feat/improved logging (#3833)
* feat(logs): add additional metadata for workflow execution logs
* Revert "Feat(logs) upgrade mothership chat messages to error (#3772)"
This reverts commit
|
||
|
|
a64afac075 |
feat(kb): harden sync engine and add connector audit logging (#3697)
* feat(kb): harden sync engine and add connector audit logging - Fix stuck syncing status: added finally block in executeSync + stale lock recovery in cron scheduler (2hr TTL) - Fix token expiry mid-sync: refresh OAuth token between pagination pages and before deferred content hydration - GitHub deferred content loading: use Git blob SHA for change detection, only fetch content for new/changed docs - Add network error keywords to isRetryableError (fetch failed, econnreset, etc.) - Extract sanitizeStorageTitle helper to fix S3 key length limit issues - Add audit logging for connector CRUD, sync triggers, document exclude/restore, and resource restoration paths * lint * fix(tests): update audit mock and route tests for new audit actions * fix(kb): address PR review - finally block race, contentHash propagation, resourceName - Replace DB-read finally block with local syncExitedCleanly flag to avoid race condition - Propagate fullDoc.contentHash during deferred content hydration - Add resourceName to file restore audit record * fix(audit): include fileId in file restore audit description |
||
|
|
5b9f0d73c2 |
feat(mothership): mothership (#3411)
* Fix lint * improvement(sidebar): loading * fix(sidebar): use client-generated UUIDs for stable optimistic updates (#3439) * fix(sidebar): use client-generated UUIDs for stable optimistic updates * fix(folders): use zod schema validation for folder create API Replace inline UUID regex with zod schema validation for consistency with other API routes. Update test expectations accordingly. * fix(sidebar): add client UUID to single workflow duplicate hook The useDuplicateWorkflow hook was missing newId: crypto.randomUUID(), causing the same temp-ID-swap issue for single workflow duplication from the context menu. * fix(folders): avoid unnecessary Set re-creation in replaceOptimisticEntry Only create new expandedFolders/selectedFolders Sets when tempId differs from data.id. In the common happy path (client-generated UUIDs), this avoids unnecessary Zustand state reference changes and re-renders. * Mothership block logs * Fix mothership block logs * improvement(knowledge): make connector-synced document chunks readonly (#3440) * improvement(knowledge): make connector-synced document chunks readonly * fix(knowledge): enforce connector chunk readonly on server side * fix(knowledge): disable toggle and delete actions for connector-synced chunks * Job exeuction logs * Job logs * fix(connectors): remove unverifiable requiredScopes for Linear connector * fix(connectors): remove legacy requiredScopes from Jira and Confluence connectors Jira and Confluence OAuth tokens don't return legacy scope names like read:jira-work or read:confluence-content.all, causing the 'Update access' banner to always appear. Set requiredScopes to empty array like Linear. * feat(tasks): add rename to task context menu (#3442) * Revert "fix(connectors): remove legacy requiredScopes from Jira and Confluence connectors" This reverts commit |
||
|
|
72bb7e6945 |
fix(executor): skip Response block formatting for internal JWT callers (#3551)
* fix(executor): skip Response block formatting for internal JWT callers
The workflow executor tool received `{error: true}` despite successful child
workflow execution when the child had a Response block. This happened because
`createHttpResponseFromBlock()` hijacked the response with raw user-defined
data, and the executor's `transformResponse` expected the standard
`{success, executionId, output, metadata}` wrapper.
Fix: skip Response block formatting when `authType === INTERNAL_JWT` since
Response blocks are designed for external API consumers, not internal
workflow-to-workflow calls. Also extract `AuthType` constants from magic
strings across all auth type comparisons in the codebase.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* test(executor): add route-level tests for Response block auth gating
Verify that internal JWT callers receive standard format while external
callers (API key, session) get Response block formatting. Tests the
server-side condition directly using workflowHasResponseBlock and
createHttpResponseFromBlock with AuthType constants.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(testing): add AuthType to all hybrid auth test mocks
Route code now imports AuthType from @/lib/auth/hybrid, so test mocks
must export it too. Added AuthTypeMock to @sim/testing and included it
in all 15 test files that mock the hybrid auth module.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
|
||
|
|
fadbad4085 |
feat(confluence): add get user by account ID tool (#3345)
* feat(confluence): add get user by account ID tool * feat(confluence): add missing tools for tasks, blog posts, spaces, descendants, permissions, and properties Add 16 new Confluence operations: list/get/update tasks, update/delete blog posts, create/update/delete spaces, get page descendants, list space permissions, list/create/delete space properties. Includes API routes, tool definitions, block config wiring, OAuth scopes, and generated docs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(confluence): add missing OAuth scopes to auth.ts provider config The OAuth authorization flow uses scopes from auth.ts, not oauth.ts. The 9 new scopes were only added to oauth.ts and the block config but not to the actual provider config in auth.ts, causing re-auth to still return tokens without the new scopes. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * lint * fix(confluence): fix truncated get_user tool description in docs Remove apostrophe from description that caused MDX generation to truncate at the escape character. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(confluence): address PR review feedback - Move get_user from GET to POST to avoid exposing access token in URL - Add 400 validation for missing params in space-properties create/delete - Add null check for blog post version before update to prevent TypeError Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(confluence): add missing response fields for descendants and tasks - Add type and depth fields to page descendants (from Confluence API) - Add body field (storage format) to task list/get/update responses Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * lint * fix(confluence): use validatePathSegment for Atlassian account IDs validateAlphanumericId rejects valid Atlassian account IDs that contain colons (e.g. 557058:6b9c9931-4693-49c1-8b3a-931f1af98134). Use validatePathSegment with a custom pattern allowing colons instead. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * ran lint * update mock * upgrade turborepo * fix(confluence): reject empty update body for space PUT Return 400 when neither name nor description is provided for space update, instead of sending an empty body to the Confluence API. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(confluence): remove spaceId requirement for create_space and fix list_tasks pagination - Remove create_space from spaceId condition array since creating a space doesn't require a space ID input - Remove list_tasks from generic supportsCursor array so it uses its dedicated handler that correctly passes assignedTo and status filters during pagination Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * ran lint * fixed type errors --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
3c470ab0f8 | fix(workflows): disallow duplicate workflow names at the same folder level (#3260) | ||
|
|
e24c824c9a |
feat(tables): added tables (#2867)
* updates * required * trashy table viewer * updates * updates * filtering ui * updates * updates * updates * one input mode * format * fix lints * improved errors * updates * updates * chages * doc strings * breaking down file * update comments with ai * updates * comments * changes * revert * updates * dedupe * updates * updates * updates * refactoring * renames & refactors * refactoring * updates * undo * update db * wand * updates * fix comments * fixes * simplify comments * u[dates * renames * better comments * validation * updates * updates * updates * fix sorting * fix appearnce * updating prompt to make it user sort * rm * updates * rename * comments * clean comments * simplicifcaiton * updates * updates * refactor * reduced type confusion * undo * rename * undo changes * undo * simplify * updates * updates * revert * updates * db updates * type fix * fix * fix error handling * updates * docs * docs * updates * rename * dedupe * revert * uncook * updates * fix * fix * fix * fix * prepare merge * readd migrations * add back missed code * migrate enrichment logic to general abstraction * address bugbot concerns * adhere to size limits for tables * remove conflicting migration * add back migrations * fix tables auth * fix permissive auth * fix lint * reran migrations * migrate to use tanstack query for all server state * update table-selector * update names * added tables to permission groups, updated subblock types --------- Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai> Co-authored-by: waleed <walif6@gmail.com> |
||
|
|
7c7c0fd955 |
feat(audit-log): add audit events for templates, billing, credentials, env, deployments, passwords (#3246)
* feat(audit-log): add audit events for templates, billing, credentials, env, deployments, passwords * improvement(audit-log): add actorName/actorEmail to all recordAudit calls * fix(audit-log): resolve user for password reset, add CREDENTIAL_SET_INVITATION_RESENT action * fix(audit-log): add workspaceId to deployment activation audit * improvement(audit-log): use better-auth callback for password reset audit, remove cast - Move password reset audit to onPasswordReset callback in auth config instead of coupling to better-auth's verification table internals - Remove ugly double-cast on workflowData.workspaceId in deployment activation * fix(audit-log): add missing actorName/actorEmail to workflow duplicate * improvement(audit-log): add resourceName to credential set invitation accept |
||
|
|
e37b4a926d |
feat(audit-log): add persistent audit log system with comprehensive route instrumentation (#3242)
* feat(audit-log): add persistent audit log system with comprehensive route instrumentation
* fix(audit-log): address PR review — nullable workspaceId, enum usage, remove redundant queries
- Make audit_log.workspace_id nullable with ON DELETE SET NULL (logs survive workspace/user deletion)
- Make audit_log.actor_id nullable with ON DELETE SET NULL
- Replace all 53 routes' string literal action/resourceType with AuditAction.X and AuditResourceType.X enums
- Fix empty workspaceId ('') → null for OAuth, form, and org routes to avoid FK violations
- Remove redundant DB queries in chat manage route (use checkChatAccess return data)
- Fix organization routes to pass workspaceId: null instead of organizationId
* fix(audit-log): replace remaining workspaceId '' fallbacks with null
* fix(audit-log): credential-set org IDs, workspace deletion FK, actorId fallback, string literal action
* reran migrations
* fix(mcp,audit): tighten env var domain bypass, add post-resolution check, form workspaceId
- Only bypass MCP domain check when env var is in hostname/authority, not path/query
- Add post-resolution validateMcpDomain call in test-connection endpoint
- Match client-side isDomainAllowed to same hostname-only bypass logic
- Return workspaceId from checkFormAccess, use in form audit logs
- Add 49 comprehensive domain-check tests covering all edge cases
* fix(mcp): stateful regex lastIndex bug, RFC 3986 authority parsing
- Remove /g flag from module-level ENV_VAR_PATTERN to avoid lastIndex state
- Create fresh regex instances per call in server-side hasEnvVarInHostname
- Fix authority extraction to terminate at /, ?, or # per RFC 3986
- Prevents bypass via https://evil.com?token={{SECRET}} (no path)
- Add test cases for query-only and fragment-only env var URLs (53 total)
* fix(audit-log): try/catch for never-throw contract, accept null actorName/Email, fix misleading action
- Wrap recordAudit body in try/catch so nanoid() or header extraction can't throw
- Accept string | null for actorName and actorEmail (session.user.name can be null)
- Normalize null -> undefined before insert to match DB column types
- Fix org members route: ORG_MEMBER_ADDED -> ORG_INVITATION_CREATED (sends invite, not adds member)
* improvement(audit-log): add resource names and specific invitation actions
* fix(audit-log): use validated chat record, add mock sync tests
|
||
|
|
36ec68d93e | fix(serializer): validate required fields for blocks without tools (#3137) | ||
|
|
8d846c5983 |
feat(async-jobs): async execution with job queue backends (#3134)
* feat(async-jobs): async execution with job queue backends * added migration * remove unused envvar, remove extraneous comments * ack comment * same for db * added dedicated async envvars for timeouts, updated helm * updated comment * ack comment * migrated routes to be more restful * ack comments --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> |
||
|
|
4db6e556b7 |
feat(canvas): added the ability to lock blocks (#3102)
* feat(canvas): added the ability to lock blocks * unlock duplicates of locked blocks * fix(duplicate): place duplicate outside locked container When duplicating a block that's inside a locked loop/parallel, the duplicate is now placed outside the container since nothing should be added to a locked container. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(duplicate): unlock all blocks when duplicating workflow - Server-side workflow duplication now sets locked: false for all blocks - regenerateWorkflowStateIds also unlocks blocks for templates - Client-side regenerateBlockIds already handled this (for paste/import) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix code block disabled state, allow unlock from editor * fix(lock): address code review feedback - Fix toggle enabled using first toggleable block, not first block - Delete button now checks isParentLocked - Lock button now has disabled state - Editor lock icon distinguishes block vs parent lock state Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(lock): prevent unlocking blocks inside locked containers - Editor: can't unlock block if parent container is locked - Action bar: can't unlock block if parent container is locked - Shows "Parent container is locked" tooltip in both cases Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(lock): ensure consistent behavior across all UIs Block Menu, Editor, Action Bar now all have identical behavior: - Enable/Disable: disabled when locked OR parent locked - Flip Handles: disabled when locked OR parent locked - Delete: disabled when locked OR parent locked - Remove from Subflow: disabled when locked OR parent locked - Lock: always available for admins - Unlock: disabled when parent is locked (unlock parent first) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(enable): consistent behavior - can't enable if parent disabled Same pattern as lock: must enable parent container first before enabling children inside it. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * docs(quick-reference): add lock block action Added documentation for the lock/unlock block feature (admin only). Note: Image placeholder added, pending actual screenshot. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * remove prefix square brackets in error notif * add lock block image * fix(block-menu): paste should not be disabled for locked selection Paste creates new blocks, doesn't modify selected ones. Changed from disableEdit (includes lock state) to !userCanEdit (permission only), matching the Duplicate action behavior. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor(workflow): extract block deletion protection into shared utility Extract duplicated block protection logic from workflow.tsx into a reusable filterProtectedBlocks helper in utils/block-protection-utils.ts. This ensures consistent behavior between context menu delete and keyboard delete operations. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor(workflow): extend block protection utilities for edge protection Add isEdgeProtected, filterUnprotectedEdges, and hasProtectedBlocks utilities. Refactor workflow.tsx to use these helpers for: - onEdgesChange edge removal filtering - onConnect connection prevention - onNodeDragStart drag prevention - Keyboard edge deletion - Block menu disableEdit calculation Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(lock): address review comments for lock feature 1. Store batchToggleEnabled now uses continue to skip locked blocks entirely, matching database operation behavior 2. Copilot add operation now checks if parent container is locked before adding nested nodes (defensive check for consistency) 3. Remove unused filterUnprotectedEdges function Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(copilot): add lock checks for insert and extract operations - insert_into_subflow: Check if existing block being moved is locked - extract_from_subflow: Check if block or parent subflow is locked These operations now match the UI behavior where locked blocks cannot be moved into/out of containers. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(lock): prevent duplicates inside locked containers via regenerateBlockIds 1. regenerateBlockIds now checks if existing parent is locked before keeping the block inside it. If parent is locked, the duplicate is placed outside (parentId cleared) instead of creating an inconsistent state. 2. Remove unnecessary effectivePermissions.canAdmin and potentialParentId from onNodeDragStart dependency array. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(lock): fix toggle locked target state and draggable check 1. BATCH_TOGGLE_LOCKED now uses first block from blocksToToggle set instead of blockIds[0], matching BATCH_TOGGLE_ENABLED pattern. Also added early exit if blocksToToggle is empty. 2. Blocks inside locked containers are now properly non-draggable. Changed draggable check from !block.locked to use isBlockProtected() which checks both block lock and parent container lock. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(copilot): check parent lock in edit and delete operations Both edit and delete operations now check if the block's parent container is locked, not just if the block itself is locked. This ensures consistent behavior with the UI which uses isBlockProtected utility that checks both direct lock and parent lock. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(socket): add server-side lock validation and admin-only permissions 1. BATCH_TOGGLE_LOCKED now requires admin role - non-admin users with write role can no longer bypass UI restriction via direct socket messages 2. BATCH_REMOVE_BLOCKS now validates lock status server-side - filters out protected blocks (locked or inside locked parent) before deletion 3. Remove duplicate/outdated comment in regenerateBlockIds Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(socket): update permission test for admin-only lock toggle batch-toggle-locked is now admin-only, so write role should be denied. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(undo-redo): use consistent target state for toggle redo The redo logic for BATCH_TOGGLE_ENABLED and BATCH_TOGGLE_LOCKED was incorrectly computing each block's new state as !previousStates[blockId]. However, the store's batchToggleEnabled/batchToggleLocked set ALL blocks to the SAME target state based on the first block's previous state. Now redo computes targetState = !previousStates[firstBlockId] and applies it to all blocks, matching the store's behavior. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(socket): add comprehensive lock validation across operations Based on audit findings, adds lock validation to multiple operations: 1. BATCH_TOGGLE_HANDLES - now skips locked/protected blocks at: - Store layer (batchToggleHandles) - Collaborative hook (collaborativeBatchToggleBlockHandles) - Server socket handler 2. BATCH_ADD_BLOCKS - server now filters blocks being added to locked parent containers 3. BATCH_UPDATE_PARENT - server now: - Skips protected blocks (locked or inside locked container) - Prevents moving blocks into locked containers All validations use consistent isProtected() helper that checks both direct lock and parent container lock. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor(workflow): use pre-computed lock state from contextMenuBlocks contextMenuBlocks already has locked and isParentLocked properties computed in use-canvas-context-menu.ts, so there's no need to look up blocks again via hasProtectedBlocks. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(lock): add lock validation to block rename operations Defense-in-depth: although the UI disables rename for locked blocks, the collaborative layer and server now also validate locks. - collaborativeUpdateBlockName: checks if block is locked or inside locked container before attempting rename - UPDATE_NAME server handler: checks lock status and parent lock before performing database update Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * added defense in depth for renaming locked blocks * fix(socket): add server-side lock validation for edges and subblocks Defense-in-depth: adds lock checks to server-side handlers that were previously relying only on client-side validation. Edge operations (ADD, REMOVE, BATCH_ADD, BATCH_REMOVE): - Check if source or target blocks are protected before modifying edges Subblock updates: - Check if parent block is protected before updating subblock values Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(lock): fetch parent blocks for edge protection checks and consistent tooltip - Fixed edge operations to fetch parent blocks before checking lock status - Previously, isBlockProtected checked if parent was locked, but the parent wasn't in blocksById because only source/target blocks were fetched - Now fetches parent blocks for all four edge operations: ADD, REMOVE, BATCH_ADD_EDGES, BATCH_REMOVE_EDGES - Fixed tooltip inconsistency: changed "Run previous blocks first" to "Run upstream blocks first" in action-bar to match workflow.tsx Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * updated tooltip text for run from block * fix(lock): add lock check to duplicate button and clean up drag handler - Added lock check to duplicate button in action bar to prevent duplicating locked blocks (consistent with other edit operations) - Removed ineffective early return in onNodeDragStart since the `draggable` property on nodes already prevents dragging protected blocks - the early return was misleading as it couldn't actually stop a drag operation Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(lock): use disableEdit for duplicate in block menu Changed duplicate menu item to use disableEdit (which includes lock check) instead of !userCanEdit for consistency with action bar and other edit operations. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> |
||
|
|
6f0a093869 |
fix(llm): update router and llm_chat tool to call providers routes (#2986)
* fix(llm): update router and llm_chat tool to call providers routes * updated failing tests |
||
|
|
78e4ca9d45 |
improvement(serializer): canonical subblock, serialization cleanups, schedules/webhooks are deployment version friendly (#2848)
* hide form deployment tab from docs * progress * fix resolution * cleanup code * fix positioning * cleanup dead sockets adv mode ops * address greptile comments * fix tests plus more simplification * fix cleanup * bring back advanced mode with specific definition * revert feature flags * improvement(subblock): ui * resolver change to make all var references optional chaining * fix(webhooks/schedules): deployment version friendly * fix tests * fix credential sets with new lifecycle * prep merge * add back migration * fix display check for adv fields * fix trigger vs block scoping --------- Co-authored-by: Emir Karabeg <emirkarabeg@berkeley.edu> |