`turbo prune sim --docker` strips `scripts/` from the pruned output (sim
doesn't depend on it), but the pruned root package.json still listed it
as a workspace, causing `bun install` to fail with "Workspace not found
'scripts'" in the Docker build.
scripts/ is dev-only tooling that runs from the repo root via `bun run
scripts/*.ts`. Its imports (glob, yaml) resolve against the root
node_modules — they're already in root devDependencies.
- Remove "scripts" from root workspaces array
- Delete scripts/package.json (no longer a workspace, manifest unused)
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
- Add pan/zoom/fit controls to mermaid diagrams rendered inline in markdown — same experience as the standalone .mmd viewer
- Wrap inline markdown images in ZoomablePreview with fit-to-container scale
- Allow fit zoom to upscale small diagrams to fill the view (previously capped at 100%)
* feat(sap): add SAP Concur integration block and SAP S/4HANA validation fixes
* added
* fix(sap_s4hana): preserve raw Set-Cookie array for CSRF cookie join
SecureFetchHeaders previously collapsed multi-value Set-Cookie headers
with ", ", forcing consumers to re-split via a fragile regex. Cookie
values containing "=" or "," (e.g., Base64 session tokens) could be
misparsed and produce malformed Cookie strings on CSRF-protected
mutations.
Add SecureFetchHeaders.getSetCookie() that returns the raw array, and
update the S/4HANA OData proxy's joinSetCookies to consume it directly.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(sap-concur): rename misleading exchange-rate tool, drop unusable refresh_token grant, validate geolocation host
- Rename sap_concur_get_exchange_rate to sap_concur_upload_exchange_rates (POST bulk upload, not GET)
- Remove refresh_token from SapConcurGrantType / Zod enum / block dropdown / docs (no implementation)
- Validate Concur geolocation hostname against SAP_CONCUR_ALLOWED_DATACENTERS
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* finished
* docs
* fix(docs): escape braces in tool/trigger description prose for MDX
Tool and trigger descriptions can contain URL path placeholders like
{reportId} or JSON-shape hints like { Items, NextPage }. When rendered
as MDX prose (not table cells), these were emitted unescaped and MDX
parsed them as JSX expressions, failing prerender with
"ReferenceError: reportId is not defined".
Escape { and } in the operation-level description and trigger
description renderers, matching the existing escaping in table-cell
descriptions.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(sap-concur): align with live API on travel-profile, itineraries, and context types
- list_travel_profiles_summary: rename Status query to Active with 1/0 values, tighten LastModifiedDate format hint
- list_itineraries / get_itinerary: use documented userid_type / userid_value / ItemsPerPage / Page query keys
- create_report_comment: contextType allows MANAGER (move to EXPENSE_READ_CONTEXT_TYPE_OPS)
- get_list_item: drop unused listId from block (tool only needs itemId)
- Tighten description copy on list_expenses/get_itemizations/associate_attendees/remove_all_attendees
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(sap-concur): correct Cash Advance v4.1 paths, add SCIM filter param
- Update Cash Advance create/get/issue tools from /cashadvance/v4/ to /cashadvance/v4.1/ to match the live API
- Add filter query param to list_users (SCIM v4.1 supports filtering by userName, employeeNumber, externalId)
- Regenerate docs MDX
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(sap-concur): drop SCIM list_users filter param (not supported on v4.1 GET)
SCIM Identity v4.1 GET /Users does not accept a filter query parameter — filtering
is only supported via POST /Users/.search (already exposed by sap_concur_search_users).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(sap-concur): final live-API alignment
Verified against live SAP Concur docs (concur/developer.concur.com preview branch):
- Revert Cash Advance paths to /cashadvance/v4/ (v4.1 endpoints do not exist; live spec is v4)
- Travel Profile v2 summary has no Active/Status query param — drop the filter from tool, types, and block
- Report Comments v4 contextType is TRAVELER or PROXY only (NOT MANAGER) — move create_report_comment + list_report_comments into the TRAVELER/PROXY context group
- Trip v1.1 query keys: userid_type / userid_value / ItemsPerPage / Page (snake/Pascal per docs) — already correct, kept
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* docs
* fix(sap-concur): restore Cash Advance v4.1 paths
Re-verified against live developer.concur.com docs at /api-reference/cash-advance/v4-1.cash-advance.html — only v4.1 endpoints are documented:
- POST /cashadvance/v4.1/cashadvances
- GET /cashadvance/v4.1/cashadvances/{cashAdvanceId}
- POST /cashadvance/v4.1/cashadvances/{cashAdvanceId}/issue
The /cashadvance/v4/ docs page returns 404. Reverts the prior local rollback in 9ef3a11d7.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* fix(security): xlsx CVE bump and bundled security hardening
* fix(stripe): use configured secret key for SDK init
Avoids leaving a recognisable placeholder string in heap dumps and
error serialisations. Webhook verification remains a purely local
HMAC operation; the SDK's constructor key is unused by it.
Addresses Greptile feedback on #4481.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(stripe): use static Stripe.webhooks for verification
Avoids instantiating a Stripe client just to access constructEvent.
The webhook signing secret is per-trigger (user-provided whsec_…) and
unrelated to our billing STRIPE_SECRET_KEY, so coupling them was wrong.
Stripe.webhooks is exposed as a static — no client, no API key needed.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(ci): revert client-bundled tools to avoid .server import in client
* fix(security): collapse 403 to 404 on v1 detail-by-ID routes
* chore(security): remove unused validateAgiloftInstanceUrl helper
* fix(security): bump minimatch + clean up scripts/ workspace
Resolves CVE-2026-27903 (GHSA-7r86-cg39-jmmj) by adding a root-level
minimatch ^10.2.5 override. Also resolves CVE-2026-0969 in next-mdx-remote
(bumped to ^6.0.0).
Cleanup:
- Make scripts/ a proper bun workspace (root workspaces array)
- Remove duplicate scripts/package-lock.json (this repo uses bun)
- Remove redundant scripts/bun.lock (now hoisted to root)
- Remove vestigial scripts/setup-doc-generator.sh
- Slim scripts/package.json to its real deps (glob, yaml)
- Gitignore stray package-lock.json files
- Update scripts/README.md
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* improvement(seo): restore explicit AI/search bot allow-list and add link-preview rules
* fix(seo): correct xAI UA strings, drop Bravebot, block /playground/ and /w/ from link-preview bots
* fix(seo): drop unverified Grok UAs, correct DeepSeekBot and ImagesiftBot tokens
* fix(seo): re-add Bravebot to allow-list per Brave Search docs
* improvement(seo): drop redundant named AI/search bot allow-list
* chore(seo): trim verbose comments in robots.ts
* fix(office-excel): support Office.js add-in embed and surface Graph errors
* fix(office-excel): delegate to parseGraphErrorFromData and handle array embed param
- drop redundant turbopack config (Next 16 defaults)
- remove lucide-react/date-fns from optimizePackageImports (built-in defaults)
- enable turbopackFileSystemCacheForBuild for warm CI builds
- disable poweredByHeader
- swap actions/cache for Blacksmith sticky disk on .next/cache
* chore(deps): upgrade next.js to 16.2.4
- Bump next and @next/env to 16.2.4 across root, apps/sim, apps/docs
- Replace next-runtime-env's env() helper (calls unstable_noStore(), rejected by Next 16.2 outside request scope) with a direct window.__ENV / process.env getter
- Add export const dynamic = 'force-dynamic' on landing /privacy and /terms pages so NEXT_PUBLIC_* runtime env reads aren't baked at build
* fix(whitelabel): force dynamic rendering for manifest.ts
Without this, NEXT_PUBLIC_BRAND_* values are baked into the manifest at build time. Pairs with the next-runtime-env removal in the prior commit, restoring Docker runtime injection for whitelabel deployments.
* fix(oauth): wrap consent page useSearchParams in Suspense
Next 16.2's stricter prerender check fails the build when useSearchParams() is used without a Suspense boundary. Splits the client component into an outer wrapper and inner body.
* fix(whitelabel): force dynamic rendering for landing segment
Client components in (landing) (e.g. Navbar) read NEXT_PUBLIC_BRAND_* via getEnv. Without this, SSR prerender would bake the build-time process.env values into HTML, mismatching window.__ENV after hydration in Docker runtime-env deployments. Cascades to all landing routes via the layout.
* revert(whitelabel): drop force-dynamic from landing layout
Cascading force-dynamic neutered dynamicParams = false + generateStaticParams on /blog/[slug], /integrations/[slug], /models/[provider], /models/[provider]/[model] — killing static prerender for SEO-critical pages. The hydration concern only materializes for whitelabel Docker deployments where build-time and runtime NEXT_PUBLIC_BRAND_* differ; those deployments can set the vars at build instead. Keeping force-dynamic on /privacy, /terms, and /manifest where it actually matters.
* fix(prerender): wrap useSearchParams callsites for Next 16.2
Next 16.2 fails the build when a client component using useSearchParams() is statically prerendered without a Suspense boundary.
- Wrap landing Navbar in Suspense (imported by /oauth/consent and other pages)
- Add force-dynamic to reset-password, invite/[id], and unsubscribe pages whose client bodies call useSearchParams
* fix(navbar): preserve SSR HTML, drop Suspense bailout
Reading useSearchParams() forced a Suspense fallback that emitted no navbar HTML during SSR — leaving crawlers and no-JS users without nav. The 'home' query param only affects client-side link targets, so read it from window.location in an effect after hydration. Restores full SSR navbar markup.
* chore: trim verbose comments in next.js upgrade
The force-dynamic export name is self-documenting; the remaining env.ts comment is tightened to the essential WHY (why we don't use next-runtime-env's helper).
* 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
* fix: use context variables for block outputs in function block code
When a function block references another block's output via <BlockA.result>,
the executor previously embedded the full value as a JavaScript literal
directly in the code string. For large outputs (>50 KB), this caused the code
string to exceed the terminal console display limit, making inputs appear
truncated or replaced with { __simTruncated: true } in the UI.
Instead, block output references in function block code are now stored as
named global variables (__blockRef_N) in the isolated VM context. The code
string only contains the compact variable name, keeping it small regardless
of the referenced value size.
Loop/parallel/env/workflow references are still inlined as literals since
the API route has no way to resolve them independently.
The _runtimeContextVars key is filtered from sanitizeInputsForLog so it
does not appear in execution logs or SSE events.
Pre-resolved context variables are merged with any variables produced by
the API route resolveCodeVariables, with executor values taking precedence.
Fixes#4195
* fix: address Cursor and Greptile bot review comments
- Pass preResolvedContextVariables through to shellEnvs for Shell language
(Cursor: shell loses pre-resolved block refs, executes against undefined vars)
- Remove duplicate CodeExecutionOutput interface declaration
(Cursor + Greptile: dead duplicate declaration in tools/function/types.ts)
- Deduplicate identical block references in resolveCodeWithContextVars so the
same <BlockA.result> reused multiple times shares one __blockRef_N slot
(Greptile P2: avoid duplicating large payloads across the wire)
* fix: shell block references and complex env value serialization
Two follow-ups to the function-block context-variable refactor:
- resolveCodeWithContextVars now emits `$__blockRef_N` for shell
function blocks so the script dereferences the env var injected
by the executor. Other languages still receive the bare identifier.
- The function-execute route now JSON-stringifies non-primitive
values when building shell env vars, replacing the previous
`String(v)` call that produced `[object Object]` for objects/arrays.
Co-Authored-By: Octopus <liyuan851277048@icloud.com>
* fix lint
* review pass
* ignore shell comments
* update contract
* fix tests
---------
Co-authored-by: Waleed <walif6@gmail.com>
Co-authored-by: Theodore Li <theodoreqili@gmail.com>
Co-authored-by: Siddharth Ganesan <33737564+Sg312@users.noreply.github.com>
Co-authored-by: Vikhyath Mondreti <vikhyathvikku@gmail.com>
Co-authored-by: octo-patch <octo-patch@github.com>
Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
Catch Postgres 23505 on insert/update so concurrent name conflicts
return a clean 409 instead of a 500. The data_drains_org_name_unique
index already prevents duplicate rows; this just improves the UX.
* fix(mothership): enforce ownership check on workflow resource attachments
* fix(mothership): fix table and knowledgebase BOLA in resource attachment resolution
* fix(mothership): apply workspace scope to table in processContextsServer
* fix(mothership): verify workspace membership before resolving workspace branch
* fix(data-drains): use const for timeoutId in sleepUntilAborted
* fix(test): mock db.select and drizzle and for workspace permissions check
* fix(mothership): always derive workspace from workflow record in workflow branch
* fix(tables): decouple master checkbox from cell-range, add allRowsSelected flag
Master checkbox detached from gutter selection state when rows or columns
changed after Cmd+A: the predicate matched normalizedSelection bounds
exactly (endRow === rows.length-1, endCol === displayColumns.length-1),
so any post-selection growth flipped it false while the cell-range
overlay still painted every row checked.
Replace the structural two-branch predicate with an explicit
allRowsSelected flag plus a uniform set-membership check. handleSelectAllRows
sets the flag in O(1); handleRowToggle materializes checkedRows when
toggling out of "all" mode. Bulk-op read sites (delete, copy, cut,
selectedRowCount) honor the flag.
Decouple gutter checkbox from cell-range drag: dragging cells no longer
fills gutter checkboxes — they reflect explicit row-selection intent
only, matching Sheets/Airtable. Cell-range overlay still paints cells.
* refactor(tables): row selection as discriminated union
Collapse `checkedRows: Set<string>` + `allRowsSelected: boolean` into a
single `RowSelection = { kind: 'none' | 'some' | 'all' }`. Impossible
states (all + non-empty Set) become unrepresentable; predicates like
`rowSelectionIncludes` and `rowSelectionIsEmpty` replace ad-hoc checks at
every read site.
* fix(tables): clear row selection after context-menu delete
handleContextMenuDelete dispatched the delete but left rowSelection at
its prior 'all' or 'some' state. After rows clear and a new row arrives
(realtime, undo, append), rowSelectionIncludes returned true for it,
rendering it checked and flipping the master checkbox back on.
* chore(tables): address review nits on row selection refactor
- guard selectedRowCount 'all' branch on contextRow membership in rows
- restore blank line between row-selection helpers and constants
* chore(tables): rename rowSelectionChanged to cellRangeRowChanged
The helper compares NormalizedSelection (cell-range) state for a given
row, not RowSelection. The old name collided with the new row-selection
discriminated union and read ambiguously.
* fix(tables): guard context-menu delete on stale rows, preserve selection on cancel
- Guard the kind='all' branch on contextRow membership in currentRows
(matches the same fix applied to selectedRowCount), so a context menu
on a stale row no longer deletes the entire table.
- Drop the eager rowSelection clear at modal-open time. The modal's
onSuccess already calls handleClearSelection after the mutation
resolves, so the post-delete invariant still holds; if the user
cancels, the selection is now preserved.
* fix(security): block IPv4-compatible IPv6 SSRF bypass
* fix(security): also block IPv4-compatible IPv6 with Class E embedded IPv4
* fix(security): correct RFC1918 test label for IPv4-compat IPv6
* improvement(confluence): expand scopes, persist canonical mode toggle
* improvement(confluence): memoize persisted canonical modes parse
* fix(confluence): paginate space selector dropdown
Confluence v2 spaces endpoint caps at limit=250 per page. The selector
endpoint was making one request and silently dropping every space past
the first page, which is why some spaces only worked when entered as a
manual spaceKey. Now follows _links.next cursor up to 20 pages (5000
spaces).
* fix(confluence): include archived spaces in selector dropdown
Confluence v2 /spaces defaults to status=current and the status param
is a single-value enum, so archived spaces never surface. They synced
fine when entered manually as a spaceKey because the connector looks
up spaces via ?keys=<key> which ignores status. Now fetches current
and archived in parallel and tags archived ones in the dropdown label.
* improvement(confluence): stream paginated space selector results
Bake pagination support into the selector abstraction via an opt-in
fetchPage definition so dropdowns populate progressively instead of
blocking on a full page-walk. Confluence spaces now stream current
then archived in a single cursor sequence.
* 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>
* chore(docs): upgrade fumadocs to latest minor versions
- fumadocs-core: 16.6.7 -> 16.8.5
- fumadocs-ui: 16.6.7 -> 16.8.5
- fumadocs-mdx: 14.2.8 -> 14.3.2
- fumadocs-openapi: 10.3.13 -> 10.8.1
- migrate deprecated sidebar.tabs to top-level tabs prop
- fix pre-existing typo (slots.paremeters) surfaced by stricter openapi types
* fix(docs): revert sidebar.tabs migration to keep deploy compat
The top-level tabs prop is only on fumadocs-ui 16.7+; deploy env was
still resolving an older type and failing typecheck. sidebar.tabs is
deprecated but still functional — keep it for now.
* fix(md-render): fix markdown rendering in file viewer
* fix(md-render): use Children.map in pre handler for robustness
* fix(md-render): add not-italic to fallback code element
* fix(md-render): fix cloneElement type error and include tables improvements
- Fix TypeScript build error: type isValidElement<Record<string, unknown>> so cloneElement accepts data-block prop
- Column sidebar: use CSS min() for responsive width instead of fixed 400px
- Table: boolean cell toggle only fires when clicking the checkbox element directly (via data-boolean-cell-toggle), not anywhere on the cell
- Table: double-clicking a boolean cell no longer opens edit mode
- Table: move row-select mousedown to the <td> to widen the hit target
- Table: run/stop button prevents row-select on mousedown
- Table: SelectAllCheckbox made keyboard-accessible; checkbox is pointer-events-none
Tool params were named `personalApiKey` but the subBlock resolves to canonical
`apiKey`, so canonical-group resolution wrote the value to params.apiKey while
the validator looked up params.personalApiKey and reported it missing.
Renames `personalApiKey` -> `apiKey` in get_person, query, list_persons,
delete_person, and types.ts. Also tightens check-block-registry.ts so a
subBlock with canonicalParamId no longer satisfies a tool param lookup by its
raw id (the raw id is deleted during extraction).
* 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(credentials): add Atlassian service account credentials
* improvement(credentials): tighten Atlassian service account plumbing
- Collapse fetchOAuthTokenBundle into fetchOAuthToken (returns the bundle)
- Reuse serviceAccountJsonSchema in the JSON form instead of hand-rolled checks
- Use parseAtlassianErrorMessage for log details; drop one-line bearer helper
- Extract ATLASSIAN_SERVICE_ACCOUNT_PROVIDER_ID/_SECRET_TYPE constants
- Use Drizzle .returning() instead of post-insert SELECT
- Helper for the duplicated 401/403 + non-OK pattern in the validator
* docs(credentials): add Atlassian service account setup guide
- New /integrations/atlassian-service-account doc covers token creation,
scope selection, and adding the credential to Sim
- Form's "View setup guide" link now points at the doc
- Fix the existing Google form link that pointed to the wrong path
Screenshot TODOs left inline as MDX comments for the docs team.
* docs(credentials): add Atlassian service account screenshots
- Auth type picker, Sim add-credential modal, Jira block credential dropdown
- Scope-picker screenshot still TODO
* docs(credentials): add Atlassian scope picker screenshot
* fix(credentials): address greptile feedback on Atlassian SA
- Drop stale 'email and API token' copy from the service description
(we only collect a token + domain, no email field)
- Move duplicate display-name check inside the create transaction so
concurrent POSTs can't both pass the check and insert duplicates
* fix(docs): move Atlassian screenshots to docs/public
Docs site serves /static/* from apps/docs/public, not apps/sim/public —
matches the existing google-service-account screenshot convention.
* fix(credentials): address review feedback on Atlassian SA
- SSRF: only accept *.atlassian.net / *.jira-dev.com hosts before fetching
tenant_info, blocking probes against localhost/internal IPs
- Confluence spaces selector: pull cloudId from the SA secret instead of
calling accessible-resources, which 401s for scoped service-account tokens
- Case-insensitive https?:// strip so HTTPS://team.atlassian.net normalizes
correctly
* chore: merge staging and bump API validation route baseline to 727
* perf(credentials): single-resolve in confluence spaces selector
Atlassian SAs were hitting resolveOAuthAccountId twice (once via
refreshAccessTokenIfNeeded, once directly to read cloudId) and
decrypting the secret twice (via getAtlassianServiceAccountToken
inside refresh, then again via getAtlassianServiceAccountSecret).
Resolve once up front and branch the whole flow on the result —
SA path skips refresh entirely and pulls token+cloudId from a
single secret read.
* refactor(credentials): consolidate Atlassian SA creation into /api/credentials
Atlassian service-account creation lived in its own route, contract, and
mutation hook, copy-pasting ~140 lines of insert/membership/audit/posthog
boilerplate from /api/credentials. Two endpoints means two authz paths,
two audit shapes, two TOCTOU stories — they will drift.
Fold Atlassian into the existing service_account branch of /api/credentials,
dispatching by providerId. The Atlassian validator (tenant_info + Bearer
/myself, SSRF host allowlist, typed error codes) lives in
lib/credentials/atlassian-service-account.ts and is the only Atlassian-
specific piece left. AtlassianValidationError maps to a {code, error} 400
in the existing catch block; the rest of the flow (transaction, members,
audit, posthog, dup-check) is now shared with Google SA + env credentials.
Delete:
- /api/auth/atlassian-service-account route
- contracts/atlassian-service-account.ts + barrel export
- useCreateAtlassianServiceAccount hook
- API audit baseline 727 → 726
Both forms (Google JSON-key, Atlassian token+domain) now call
useCreateWorkspaceCredential with the appropriate body shape.
* fix(credentials): close TOCTOU and restore typed errors after consolidation
- Add inner duplicate-guard inside the create transaction (DuplicateCredentialError)
to close the race that the outer findExistingCredentialBySource leaves open.
service_account rows have no DB-level unique index on (workspaceId, providerId,
displayName), so this is the actual safety net. Tx-internal check applies to
Google + env_workspace too — race-safety win for all credential types.
- Re-emit {code: 'duplicate_display_name', error: ...} on conflict so the form's
ERROR_MESSAGES.duplicate_display_name mapping is reachable again.
- Thread Atlassian-specific audit metadata (atlassianDomain, atlassianCloudId)
back into recordAudit; consolidation had dropped them.
- Use ATLASSIAN_SERVICE_ACCOUNT_PROVIDER_ID constant in contract superRefine.
- Drop `error: any` in catch in favor of `error: unknown` + getPostgresErrorCode.
* chore(credentials): drop dead createWorkspaceCredentialBodySchema + updateWorkspaceCredentialBodySchema
Both shadowed the actually-used schemas (createCredentialBodySchema /
updateCredentialByIdBodySchema) and were missing the apiToken/domain
Atlassian fields. A future change could pick the wrong one and silently
drop those fields. Confirmed zero non-definition references in the repo
(grep across apps/, packages/, scripts/ minus build artifacts).
* fix(credentials): scope inner duplicate re-check to service_account
OAuth dedupes by accountId, env_* by envKey — both have DB-level partial
unique indexes that surface as 23505. The previous inner re-check fired
for all types and always threw DuplicateCredentialError, which mapped to
'duplicate_display_name' in the UI even when the real conflict was a
duplicate OAuth account or env key. Restrict the in-tx re-check to
service_account (the only type without a DB-level index) and let the
23505 handler emit a generic message for everything else.
---------
Co-authored-by: Waleed <walif6@gmail.com>
Co-authored-by: Siddharth Ganesan <33737564+Sg312@users.noreply.github.com>
Co-authored-by: Vikhyath Mondreti <vikhyathvikku@gmail.com>
* feat(posthog): correlate task events with Go logs via request_id
Auto-injects server request_id into all PostHog server events from
AsyncLocalStorage context. Adds task_request_started client event fired
when SSE traceparent response header arrives, carrying the trace ID used
by Go for log correlation. task_generation_aborted now includes the
in-flight request_id when available.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* improvement(posthog): tighten trace ID parse and verbose comments
* fix(posthog): use traceparent header as canonical request_id source
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* refactor(tables): phase 1 — gutter numbering from array index
Delete the PositionGapRows component, the gap-fill loop, and the
GAP_CHECKBOX_CLASS / GAP_ROW_LIMIT / PositionGapRowsProps surface area.
The server's recompactPositions() guarantees positions are 0..N-1
contiguous in the unfiltered view, so the phantom-row machinery has
been defending against a state that essentially never happens.
DataRow now receives an arrayIndex prop and renders {arrayIndex + 1}
in the gutter. Selection coordinates still flow through row.position;
that switches in phase 2.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* refactor(tables): phase 2-5 — selection by array index, checks by row id
Decouple Tables UI selection coordinates from DB position:
- rowIndex semantics shift from row.position to array index across
selection state, mouse handlers, keyboard nav, paste, scrollIntoView
- checkedRows: Set<number> (position) → Set<string> (rowId), survives
sort/filter and realtime row inserts
- lastCheckboxRowRef stores rowId; shift-click range resolves to current
array indices for visual-order ranges
- Drop positionMap/maxPosition derived state in favor of direct rowsRef
reads
- ExpandedCellPopover anchors via data-row-id (row-id-stable) instead
of data-row (array index)
- collectRowSnapshots accepts Iterable<TableRowType> directly
- Add bounds-validation effect to clamp anchor/focus when rows.length
shrinks (sort change, pagination, realtime delete)
- Drop redundant arrayIndex prop on DataRow (rowIndex now equals it)
Server-side position math stays at API boundary only: insertRow,
duplicateRow, shift-Enter append, paste create-batch, undo snapshots.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* refactor(tables): rowId-stable selection across sort, position math via reduce
- Track anchor/focus by rowId so same-length sort changes remap selection
to the new visual index instead of leaving it on a different row.
- Replace last-row position lookups with Math.max reduce in paste's
create-batch and append-row's undo snapshot — under non-position sorts,
the last visual row's position is not the largest.
- Trim a navigation-noise comment and tighten two over-explanatory ones.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(tables): guard rowId remap effect and document paste batch position
- Skip the validation effect when rows is empty (transient state during
initial load of a new sort/filter before keepPreviousData populates) so
selection survives uncached query changes.
- Skip when isColumnSelection is true; the column-selection pinning effect
owns focus.rowIndex for those, and remapping would shrink a full-column
range to wherever the captured endpoints happened to land after reorder.
- Comment lastRowPosition's hoist invariant so a future refactor doesn't
move it inside the loop and produce colliding positions.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* fix(tables): suppress phantom rows on sort, center gutter numbers, stop select-all viewport jump
* fix(tables): suppress scroll on Ctrl+A select-all
Cmd/Ctrl+A duplicates the select-all logic but missed the
suppressFocusScrollRef flag, so the keyboard path still triggered
the viewport jump.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* feat(logs): add Logs block for querying execution logs from workflows
* fix(logs): guard transformResponse on non-2xx and correct executionMetadata description
- Add response.ok check in all three logs tools' transformResponse so a
4xx/5xx body cannot be silently treated as a success payload (defense
in depth; the executor already throws on non-2xx before transform runs).
- Drop totalTokens from executionMetadata description in block and tool
outputs since the snapshot route does not emit it.
* fix(logs): split summary/detail contracts to make trace tab gate type-safe
The Trace tab was silently missing from the Log Details sidepanel because
list and detail rows shared one WorkflowLog type with executionData:
z.unknown(). The UI couldn't distinguish a summary row (no spans) from a
detail row (with spans), so the tab gate read undefined and hid itself.
Splits into WorkflowLogSummary (list) and WorkflowLogDetail (typed
executionData with optional traceSpans). Detail and by-execution routes
both write through to the same logKeys.detail(id) cache, eliminating the
two-key fragmentation that caused the merge memo workaround. List route
moves to cursor pagination on (sortValue, id) with proper NULLS LAST
handling and SQL-side sort across workflow + job execution tables.
Detail route now requires and asserts workspaceId. Deep-link path uses
useLogByExecutionId instead of auto-paginating the entire workspace.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(logs): audit follow-ups — render side-effect, stats invalidation, enhanced spread order
- Move onActiveTabChange call from render into useEffect to avoid
side-effects during render (StrictMode safety).
- Re-add logKeys.stats() invalidation to cancel/retry mutations so
the dashboard reflects status flips immediately.
- Reorder enhanced: true after ...execData spread in detail and
by-execution routes so the literal discriminator is never
overwritten by stale execData.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(logs): mirror SQL NULLS LAST in JS merge for cursor consistency
The in-memory merge of workflow + job pages negated the comparator
for DESC, which placed null sort values at the start. SQL orders
both ASC and DESC with NULLS LAST, so DESC pages emitted a cursor
{v: <last non-null>, id: ...} while null rows still satisfied the
cursor predicate (OR sort_expr IS NULL) on the next page —
producing duplicate null rows across pages on cost/duration sorts.
Handle nulls explicitly in the JS comparator so they always sort
last regardless of direction, matching the SQL ordering.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(logs): final-audit follow-ups — stable tab callback, byExecution invalidation, optimistic detail patch, trace loading state
- Wrap LogDetails -> LogDetailsContent onActiveTabChange in useCallback
so the child useEffect doesn't refire on every parent render.
- Add logKeys.byExecutionAll() to cancel + retry invalidation so the
table-embedded sidebar picks up status changes immediately.
- Optimistic write-through to logKeys.detail in useCancelExecution so
the open sidebar reflects 'cancelling' instantly; rolls back on error.
- Distinguish trace loading from trace-empty: when log.executionData is
not yet fetched, render "Loading trace…" instead of the empty state.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* refactor(logs): migrate stores/components to contract types
Replace the legacy `WorkflowLog` / `LogsResponse` / `WorkflowData` /
`CostMetadata` / `ToolCallMetadata` shapes in
`stores/logs/filters/types.ts` with direct use of the contract types
`WorkflowLogSummary`, `WorkflowLogDetail`, and a new `WorkflowLogRow`
alias for surfaces that render either form. Removes the
`summaryToWorkflowLog` / `detailToWorkflowLog` bridge in the React
Query layer along with their double-cast annotations.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* refactor(logs): address PR review feedback
- Whitelist sort columns against logSortBy enum to prevent client crash
when non-sortable headers (workflow, trigger) reach the contract parser.
- Extract fetchLogDetail helper shared by /api/logs/[id] and
/api/logs/by-execution/[executionId] — collapses ~360 duplicated lines
to a single source of truth keyed on lookup column.
* fix(logs): exclude job logs when level filter is workflow-only
When level=running or level=pending (workflow-only states involving
endedAt/pausedExecutions semantics), jobLevelConditions stayed empty
so no level constraint reached jobConditions — every job log in the
workspace leaked into the result. Skip the job side entirely when the
level filter has no job-applicable values (error/info).
* chore(logs): drop dead utils — mapToExecutionLog and friends
Remove ExecutionLog/RawLogResponse/ExecutionCost/LogWithExecutionData/
TraceSpan/BlockExecution interfaces and the mapToExecutionLog,
mapToExecutionLogAlt, extractOutput functions — all unreferenced after
the contract split. -212 lines.
* chore(logs): drop unused LOG_COLUMN_ORDER and LogColumnKey
* fix(logs): hydrate filters from URL synchronously on mount
The previous useEffect-based initializeFromURL caused useLogsList and
useDashboardStats to fire once with default store filters, then refetch
after the effect updated filters from the URL. Move the initial hydrate
into a useState lazy initializer so the first render already reads
URL-derived filters; the popstate handler keeps the existing effect for
back/forward navigation.
* chore(logs): trim verbose comments added during PR
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(logs): guard navigation arrows when selected log is off-page
Deep-linked logs resolved via useLogByExecutionId may not be in the
current page list, leaving selectedLogIndex at -1. The hasNext prop
was evaluating -1 < logs.length - 1 (true for any non-empty list),
which enabled the next arrow and jumped to the first item on click.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(logs): sync active-tab callback before paint to keep keyboard guards aligned
Run the resolvedTab → onActiveTabChange propagation in useLayoutEffect
so the parent's activeTabRef updates synchronously before the next
paint. This closes the brief window where window keydown handlers
in the logs page would still see activeTabRef.current === 'trace'
and short-circuit arrow-key navigation immediately after switching
to a log without a Trace tab.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* fix(copilot): redact sim_key API keys from persisted Mothership chat messages
* improvement(emcn): promote ApiKeyReveal to SecretReveal in emcn
* fix(copilot): thread cursor across content blocks when restoring sim_key tags
* fix(mothership): stop persisting log resources from get_workflow_logs and self-heal stale log panel entries
* fix(mothership): skip retries on 404 in useLogDetail for instant self-heal
* fix(mothership): simplify onNotFoundRef sync to inline assignment
* fix(mothership): catch draft restore errors instead of crashing /home
Wrap the mount-time draft restore in try/catch with clearDraft on
throw, and coerce text to a string in the useState initializer.
A corrupt entry in mothership-drafts:v1 localStorage previously took
down the entire workspace via the error boundary.
* fix(mothership): defer state writes and log restore failures
Build the restored state in locals first and only apply on success
so a partial throw can't leave stale contexts in the UI with the
draft already cleared. Switch the empty catch to logger.error so
corrupt-draft incidents surface in production logs.
* feat(knowledge): expose Cohere reranker controls on knowledge block
Add a self-hosted Cohere API key field (mirroring the agent block's hosted-key
pattern), a configurable reranker input pool size (1-100), and surface
meta.warnings from Cohere rerank responses via logger.warn. All new contract
fields are optional and nullable for full backwards compatibility.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(knowledge): address PR feedback on Cohere reranker controls
- Drop required:true on apiKey field — server has BYOK→env→rotation fallback
chain, so self-hosted users with COHERE_API_KEY env should not be blocked
- Drop .min(1) on rerankerApiKey contract field so empty strings coerce to
undefined via the transform (matches the existing query field pattern)
- Log a warning when rerankerInputCount is clamped up to topK so users notice
their setting was overridden
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* feat(knowledge): mirror agent block API key visibility for Cohere reranker
Restore required:true on the Cohere API Key field and hide it server-side via
a new NEXT_PUBLIC_COHERE_CONFIGURED public env flag — same pattern the Agent
block uses for Azure (NEXT_PUBLIC_AZURE_CONFIGURED). Self-hosters who set
COHERE_API_KEY in their environment also set NEXT_PUBLIC_COHERE_CONFIGURED=true,
which removes the field from the UI; everyone else sees a required field.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(knowledge): treat empty rerankerInputCount as unset
An empty string from the Documents Sent to Reranker input passed the
undefined/null guard, so Number('') = 0 → clamped to 1, sending only 1
document to the reranker instead of falling back to the 4× topK auto
default. Add the empty-string check to the guard.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>