Compare commits

..

21 Commits

Author SHA1 Message Date
Vikhyath Mondreti
8c0a2e04b1 v0.5.108: workflow input params in agent tools, bun upgrade, dropdown selectors for 14 blocks 2026-03-06 21:02:25 -08:00
Waleed
0a52b09deb feat(jira): add search_users tool for user lookup by email (#3451)
* feat(jira): add search_users tool for user lookup by email

* improvement(jira): reuse shared transformUser utility in search_users

* improvement(jira): add pagination fields to search_users response

* update

* fix(jira): filter falsy entries before transforming search_users results

* fix(jira): add defensive fallback for nullable transformUser in search_users

* fix(jira): align search_users response type with transformUser return type
2026-03-06 19:52:37 -08:00
Vikhyath Mondreti
1d36b80172 improvement(selectors): remove dead semantic fallback code (#3454)
* improvement(selectors): simplify selectorContext + add tests

* fix resolve values fallback

* another workflowid pass through

* remove dead code

* make workspace id required
2026-03-06 19:38:57 -08:00
Vikhyath Mondreti
e6a5e7f4e4 improvement(selectors): simplify selector context + add tests (#3453)
* improvement(selectors): simplify selectorContext + add tests

* fix resolve values fallback

* another workflowid pass through
2026-03-06 18:30:46 -08:00
Waleed
6586c5ce40 v0.5.107: new reddit, slack tools 2026-03-05 22:48:20 -08:00
Vikhyath Mondreti
3ce947566d v0.5.106: condition block and legacy kbs fixes, GPT 5.4 2026-03-05 17:30:05 -08:00
Waleed
70c36cb7aa v0.5.105: slack remove reaction, nested subflow locks fix, servicenow pagination, memory improvements 2026-03-04 22:38:26 -08:00
Waleed
f1ec5fe824 v0.5.104: memory improvements, nested subflows, careers page redirect, brandfetch, google meet 2026-03-03 23:45:29 -08:00
Waleed
e07e3c34cc v0.5.103: memory util instrumentation, API docs, amplitude, google pagespeed insights, pagerduty 2026-03-01 23:27:02 -08:00
Waleed
0d2e6ff31d v0.5.102: new integrations, new tools, ci speedups, memory leak instrumentation 2026-02-28 12:48:10 -08:00
Waleed
4fd0989264 v0.5.101: circular dependency mitigation, confluence enhancements, google tasks and bigquery integrations, workflow lock 2026-02-26 15:04:53 -08:00
Waleed
67f8a687f6 v0.5.100: multiple credentials, 40% speedup, gong, attio, audit log improvements 2026-02-25 00:28:25 -08:00
Waleed
af592349d3 v0.5.99: local dev improvements, live workflow logs in terminal 2026-02-23 00:24:49 -08:00
Waleed
0d86ea01f0 v0.5.98: change detection improvements, rate limit and code execution fixes, removed retired models, hex integration 2026-02-21 18:07:40 -08:00
Waleed
115f04e989 v0.5.97: oidc discovery for copilot mcp 2026-02-21 02:06:25 -08:00
Waleed
34d92fae89 v0.5.96: sim oauth provider, slack ephemeral message tool and blockkit support 2026-02-20 18:22:20 -08:00
Waleed
67aa4bb332 v0.5.95: gemini 3.1 pro, cloudflare, dataverse, revenuecat, redis, upstash, algolia tools; isolated-vm robustness improvements, tables backend (#3271)
* feat(tools): advanced fields for youtube, vercel; added cloudflare and dataverse tools (#3257)

* refactor(vercel): mark optional fields as advanced mode

Move optional/power-user fields behind the advanced toggle:
- List Deployments: project filter, target, state
- Create Deployment: project ID override, redeploy from, target
- List Projects: search
- Create/Update Project: framework, build/output/install commands
- Env Vars: variable type
- Webhooks: project IDs filter
- Checks: path, details URL
- Team Members: role filter
- All operations: team ID scope

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* style(youtube): mark optional params as advanced mode

Hide pagination, sort order, and filter fields behind the advanced
toggle for a cleaner default UX across all YouTube operations.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* added advanced fields for vercel and youtube, added cloudflare and dataverse block

* addded desc for dataverse

* add more tools

* ack comment

* more

* ops

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* 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>

* fix(snapshot): changed insert to upsert when concurrent identical child workflows are running (#3259)

* fix(snapshot): changed insert to upsert when concurrent identical child workflows are running

* fixed ci tests failing

* fix(workflows): disallow duplicate workflow names at the same folder level (#3260)

* feat(tools): added redis, upstash, algolia, and revenuecat (#3261)

* feat(tools): added redis, upstash, algolia, and revenuecat

* ack comment

* feat(models): add gemini-3.1-pro-preview and update gemini-3-pro thinking levels (#3263)

* fix(audit-log): lazily resolve actor name/email when missing (#3262)

* fix(blocks): move type coercions from tools.config.tool to tools.config.params (#3264)

* fix(blocks): move type coercions from tools.config.tool to tools.config.params

Number() coercions in tools.config.tool ran at serialization time before
variable resolution, destroying dynamic references like <block.result.count>
by converting them to NaN/null. Moved all coercions to tools.config.params
which runs at execution time after variables are resolved.

Fixed in 15 blocks: exa, arxiv, sentry, incidentio, wikipedia, ahrefs,
posthog, elasticsearch, dropbox, hunter, lemlist, spotify, youtube, grafana,
parallel. Also added mode: 'advanced' to optional exa fields.

Closes #3258

* fix(blocks): address PR review — move remaining param mutations from tool() to params()

- Moved field mappings from tool() to params() in grafana, posthog,
  lemlist, spotify, dropbox (same dynamic reference bug)
- Fixed parallel.ts excerpts/full_content boolean logic
- Fixed parallel.ts search_queries empty case (must set undefined)
- Fixed elasticsearch.ts timeout not included when already ends with 's'
- Restored dropbox.ts tool() switch for proper default fallback

* fix(blocks): restore field renames to tool() for serialization-time validation

Field renames (e.g. personalApiKey→apiKey) must be in tool() because
validateRequiredFieldsBeforeExecution calls selectToolId()→tool() then
checks renamed field names on params. Only type coercions (Number(),
boolean) stay in params() to avoid destroying dynamic variable references.

* improvement(resolver): resovled empty sentinel to not pass through unexecuted valid refs to text inputs (#3266)

* fix(blocks): add required constraint for serviceDeskId in JSM block (#3268)

* fix(blocks): add required constraint for serviceDeskId in JSM block

* fix(blocks): rename custom field values to request field values in JSM create request

* fix(trigger): add isolated-vm support to trigger.dev container builds (#3269)

Scheduled workflow executions running in trigger.dev containers were
failing to spawn isolated-vm workers because the native module wasn't
available in the container. This caused loop condition evaluation to
silently fail and exit after one iteration.

- Add isolated-vm to build.external and additionalPackages in trigger config
- Include isolated-vm-worker.cjs via additionalFiles for child process spawning
- Add fallback path resolution for worker file in trigger.dev environment

* fix(tables): hide tables from sidebar and block registry (#3270)

* fix(tables): hide tables from sidebar and block registry

* fix(trigger): add isolated-vm support to trigger.dev container builds (#3269)

Scheduled workflow executions running in trigger.dev containers were
failing to spawn isolated-vm workers because the native module wasn't
available in the container. This caused loop condition evaluation to
silently fail and exit after one iteration.

- Add isolated-vm to build.external and additionalPackages in trigger config
- Include isolated-vm-worker.cjs via additionalFiles for child process spawning
- Add fallback path resolution for worker file in trigger.dev environment

* lint

* fix(trigger): update node version to align with main app (#3272)

* fix(build): fix corrupted sticky disk cache on blacksmith (#3273)

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Lakee Sivaraya <71339072+lakeesiv@users.noreply.github.com>
Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
Co-authored-by: Vikhyath Mondreti <vikhyathvikku@gmail.com>
2026-02-20 13:43:07 -08:00
Waleed
15ace5e63f v0.5.94: vercel integration, folder insertion, migrated tracking redirects to rewrites 2026-02-18 16:53:34 -08:00
Waleed
fdca73679d v0.5.93: NextJS config changes, MCP and Blocks whitelisting, copilot keyboard shortcuts, audit logs 2026-02-18 12:10:05 -08:00
Waleed
da46a387c9 v0.5.92: shortlinks, copilot scrolling stickiness, pagination 2026-02-17 15:13:21 -08:00
Waleed
b7e377ec4b v0.5.91: docs i18n, turborepo upgrade 2026-02-16 00:36:05 -08:00
7 changed files with 290 additions and 4 deletions

View File

@@ -1014,4 +1014,36 @@ Get Jira users. If an account ID is provided, returns a single user. Otherwise,
| `startAt` | number | Pagination start index |
| `maxResults` | number | Maximum results per page |
### `jira_search_users`
Search for Jira users by email address or display name. Returns matching users with their accountId, displayName, and emailAddress.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
| `query` | string | Yes | A query string to search for users. Can be an email address, display name, or partial match. |
| `maxResults` | number | No | Maximum number of users to return \(default: 50, max: 1000\) |
| `startAt` | number | No | The index of the first user to return \(for pagination, default: 0\) |
| `cloudId` | string | No | Jira Cloud ID for the instance. If not provided, it will be fetched using the domain. |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `ts` | string | ISO 8601 timestamp of the operation |
| `users` | array | Array of matching Jira users |
| ↳ `accountId` | string | Atlassian account ID of the user |
| ↳ `displayName` | string | Display name of the user |
| ↳ `active` | boolean | Whether the user account is active |
| ↳ `emailAddress` | string | Email address of the user |
| ↳ `accountType` | string | Type of account \(e.g., atlassian, app, customer\) |
| ↳ `avatarUrl` | string | URL to the user avatar \(48x48\) |
| ↳ `timeZone` | string | User timezone |
| ↳ `self` | string | REST API URL for this user |
| `total` | number | Number of users returned in this page \(may be less than total matches\) |
| `startAt` | number | Pagination start index |
| `maxResults` | number | Maximum results per page |

View File

@@ -47,6 +47,7 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
{ label: 'Add Watcher', id: 'add_watcher' },
{ label: 'Remove Watcher', id: 'remove_watcher' },
{ label: 'Get Users', id: 'get_users' },
{ label: 'Search Users', id: 'search_users' },
],
value: () => 'read',
},
@@ -673,6 +674,31 @@ Return ONLY the comment text - no explanations.`,
placeholder: 'Maximum users to return (default: 50)',
condition: { field: 'operation', value: 'get_users' },
},
// Search Users fields
{
id: 'searchUsersQuery',
title: 'Search Query',
type: 'short-input',
required: true,
placeholder: 'Enter email address or display name to search',
condition: { field: 'operation', value: 'search_users' },
},
{
id: 'searchUsersMaxResults',
title: 'Max Results',
type: 'short-input',
placeholder: 'Maximum users to return (default: 50)',
condition: { field: 'operation', value: 'search_users' },
mode: 'advanced',
},
{
id: 'searchUsersStartAt',
title: 'Start At',
type: 'short-input',
placeholder: 'Pagination start index (default: 0)',
condition: { field: 'operation', value: 'search_users' },
mode: 'advanced',
},
// Trigger SubBlocks
...getTrigger('jira_issue_created').subBlocks,
...getTrigger('jira_issue_updated').subBlocks,
@@ -707,6 +733,7 @@ Return ONLY the comment text - no explanations.`,
'jira_add_watcher',
'jira_remove_watcher',
'jira_get_users',
'jira_search_users',
],
config: {
tool: (params) => {
@@ -767,6 +794,8 @@ Return ONLY the comment text - no explanations.`,
return 'jira_remove_watcher'
case 'get_users':
return 'jira_get_users'
case 'search_users':
return 'jira_search_users'
default:
return 'jira_retrieve'
}
@@ -1023,6 +1052,18 @@ Return ONLY the comment text - no explanations.`,
: undefined,
}
}
case 'search_users': {
return {
...baseParams,
query: params.searchUsersQuery,
maxResults: params.searchUsersMaxResults
? Number.parseInt(params.searchUsersMaxResults)
: undefined,
startAt: params.searchUsersStartAt
? Number.parseInt(params.searchUsersStartAt)
: undefined,
}
}
default:
return baseParams
}
@@ -1102,6 +1143,13 @@ Return ONLY the comment text - no explanations.`,
},
usersStartAt: { type: 'string', description: 'Pagination start index for users' },
usersMaxResults: { type: 'string', description: 'Maximum users to return' },
// Search Users operation inputs
searchUsersQuery: {
type: 'string',
description: 'Search query (email address or display name)',
},
searchUsersMaxResults: { type: 'string', description: 'Maximum users to return from search' },
searchUsersStartAt: { type: 'string', description: 'Pagination start index for user search' },
},
outputs: {
// Common outputs across all Jira operations

View File

@@ -117,6 +117,10 @@ export async function loadDeployedWorkflowState(
resolvedWorkspaceId = wfRow?.workspaceId ?? undefined
}
if (!resolvedWorkspaceId) {
throw new Error(`Workflow ${workflowId} has no workspace`)
}
const { blocks: migratedBlocks } = await applyBlockMigrations(
state.blocks || {},
resolvedWorkspaceId
@@ -139,7 +143,7 @@ export async function loadDeployedWorkflowState(
interface MigrationContext {
blocks: Record<string, BlockState>
workspaceId?: string
workspaceId: string
migrated: boolean
}
@@ -148,7 +152,7 @@ type BlockMigration = (ctx: MigrationContext) => MigrationContext | Promise<Migr
function createMigrationPipeline(migrations: BlockMigration[]) {
return async (
blocks: Record<string, BlockState>,
workspaceId?: string
workspaceId: string
): Promise<{ blocks: Record<string, BlockState>; migrated: boolean }> => {
let ctx: MigrationContext = { blocks, workspaceId, migrated: false }
for (const migration of migrations) {
@@ -170,7 +174,6 @@ const applyBlockMigrations = createMigrationPipeline([
}),
async (ctx) => {
if (!ctx.workspaceId) return ctx
const { blocks, migrated } = await migrateCredentialIds(ctx.blocks, ctx.workspaceId)
return { ...ctx, blocks, migrated: ctx.migrated || migrated }
},
@@ -409,9 +412,13 @@ export async function loadWorkflowFromNormalizedTables(
blocksMap[block.id] = assembled
})
if (!workflowRow?.workspaceId) {
throw new Error(`Workflow ${workflowId} has no workspace`)
}
const { blocks: finalBlocks, migrated } = await applyBlockMigrations(
blocksMap,
workflowRow?.workspaceId ?? undefined
workflowRow.workspaceId
)
if (migrated) {

View File

@@ -17,6 +17,7 @@ import { jiraGetWorklogsTool } from '@/tools/jira/get_worklogs'
import { jiraRemoveWatcherTool } from '@/tools/jira/remove_watcher'
import { jiraRetrieveTool } from '@/tools/jira/retrieve'
import { jiraSearchIssuesTool } from '@/tools/jira/search_issues'
import { jiraSearchUsersTool } from '@/tools/jira/search_users'
import { jiraTransitionIssueTool } from '@/tools/jira/transition_issue'
import { jiraUpdateTool } from '@/tools/jira/update'
import { jiraUpdateCommentTool } from '@/tools/jira/update_comment'
@@ -48,4 +49,5 @@ export {
jiraAddWatcherTool,
jiraRemoveWatcherTool,
jiraGetUsersTool,
jiraSearchUsersTool,
}

View File

@@ -0,0 +1,166 @@
import type { JiraSearchUsersParams, JiraSearchUsersResponse } from '@/tools/jira/types'
import { TIMESTAMP_OUTPUT, USER_OUTPUT_PROPERTIES } from '@/tools/jira/types'
import { getJiraCloudId, transformUser } from '@/tools/jira/utils'
import type { ToolConfig } from '@/tools/types'
export const jiraSearchUsersTool: ToolConfig<JiraSearchUsersParams, JiraSearchUsersResponse> = {
id: 'jira_search_users',
name: 'Jira Search Users',
description:
'Search for Jira users by email address or display name. Returns matching users with their accountId, displayName, and emailAddress.',
version: '1.0.0',
oauth: {
required: true,
provider: 'jira',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'OAuth access token for Jira',
},
domain: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your Jira domain (e.g., yourcompany.atlassian.net)',
},
query: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description:
'A query string to search for users. Can be an email address, display name, or partial match.',
},
maxResults: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of users to return (default: 50, max: 1000)',
},
startAt: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'The index of the first user to return (for pagination, default: 0)',
},
cloudId: {
type: 'string',
required: false,
visibility: 'hidden',
description:
'Jira Cloud ID for the instance. If not provided, it will be fetched using the domain.',
},
},
request: {
url: (params: JiraSearchUsersParams) => {
if (params.cloudId) {
const queryParams = new URLSearchParams()
queryParams.append('query', params.query)
if (params.maxResults !== undefined)
queryParams.append('maxResults', String(params.maxResults))
if (params.startAt !== undefined) queryParams.append('startAt', String(params.startAt))
return `https://api.atlassian.com/ex/jira/${params.cloudId}/rest/api/3/user/search?${queryParams.toString()}`
}
return 'https://api.atlassian.com/oauth/token/accessible-resources'
},
method: 'GET',
headers: (params: JiraSearchUsersParams) => ({
Accept: 'application/json',
Authorization: `Bearer ${params.accessToken}`,
}),
},
transformResponse: async (response: Response, params?: JiraSearchUsersParams) => {
const fetchUsers = async (cloudId: string) => {
const queryParams = new URLSearchParams()
queryParams.append('query', params!.query)
if (params!.maxResults !== undefined)
queryParams.append('maxResults', String(params!.maxResults))
if (params!.startAt !== undefined) queryParams.append('startAt', String(params!.startAt))
const usersUrl = `https://api.atlassian.com/ex/jira/${cloudId}/rest/api/3/user/search?${queryParams.toString()}`
const usersResponse = await fetch(usersUrl, {
method: 'GET',
headers: {
Accept: 'application/json',
Authorization: `Bearer ${params!.accessToken}`,
},
})
if (!usersResponse.ok) {
let message = `Failed to search Jira users (${usersResponse.status})`
try {
const err = await usersResponse.json()
message = err?.errorMessages?.join(', ') || err?.message || message
} catch (_e) {}
throw new Error(message)
}
return usersResponse.json()
}
let data: any
if (!params?.cloudId) {
const cloudId = await getJiraCloudId(params!.domain, params!.accessToken)
data = await fetchUsers(cloudId)
} else {
if (!response.ok) {
let message = `Failed to search Jira users (${response.status})`
try {
const err = await response.json()
message = err?.errorMessages?.join(', ') || err?.message || message
} catch (_e) {}
throw new Error(message)
}
data = await response.json()
}
const users = Array.isArray(data) ? data.filter(Boolean) : []
return {
success: true,
output: {
ts: new Date().toISOString(),
users: users.map((user: any) => ({
...(transformUser(user) ?? { accountId: '', displayName: '' }),
self: user.self ?? null,
})),
total: users.length,
startAt: params?.startAt ?? 0,
maxResults: params?.maxResults ?? 50,
},
}
},
outputs: {
ts: TIMESTAMP_OUTPUT,
users: {
type: 'array',
description: 'Array of matching Jira users',
items: {
type: 'object',
properties: {
...USER_OUTPUT_PROPERTIES,
self: {
type: 'string',
description: 'REST API URL for this user',
optional: true,
},
},
},
},
total: {
type: 'number',
description: 'Number of users returned in this page (may be less than total matches)',
},
startAt: { type: 'number', description: 'Pagination start index' },
maxResults: { type: 'number', description: 'Maximum results per page' },
},
}

View File

@@ -1549,6 +1549,34 @@ export interface JiraGetUsersParams {
cloudId?: string
}
export interface JiraSearchUsersParams {
accessToken: string
domain: string
query: string
maxResults?: number
startAt?: number
cloudId?: string
}
export interface JiraSearchUsersResponse extends ToolResponse {
output: {
ts: string
users: Array<{
accountId: string
accountType?: string | null
active?: boolean | null
displayName: string
emailAddress?: string | null
avatarUrl?: string | null
timeZone?: string | null
self?: string | null
}>
total: number
startAt: number
maxResults: number
}
}
export interface JiraGetUsersResponse extends ToolResponse {
output: {
ts: string
@@ -1594,3 +1622,4 @@ export type JiraResponse =
| JiraAddWatcherResponse
| JiraRemoveWatcherResponse
| JiraGetUsersResponse
| JiraSearchUsersResponse

View File

@@ -1085,6 +1085,7 @@ import {
jiraRemoveWatcherTool,
jiraRetrieveTool,
jiraSearchIssuesTool,
jiraSearchUsersTool,
jiraTransitionIssueTool,
jiraUpdateCommentTool,
jiraUpdateTool,
@@ -2536,6 +2537,7 @@ export const tools: Record<string, ToolConfig> = {
jira_add_watcher: jiraAddWatcherTool,
jira_remove_watcher: jiraRemoveWatcherTool,
jira_get_users: jiraGetUsersTool,
jira_search_users: jiraSearchUsersTool,
jsm_get_service_desks: jsmGetServiceDesksTool,
jsm_get_request_types: jsmGetRequestTypesTool,
jsm_get_request_type_fields: jsmGetRequestTypeFieldsTool,