Compare commits

...

14 Commits

Author SHA1 Message Date
Waleed
0d2e6ff31d v0.5.102: new integrations, new tools, ci speedups, memory leak instrumentation 2026-02-28 12:48:10 -08:00
Waleed
9be75e3633 improvement(loops): validate loops integration and update skill files (#3384)
* improvement(loops): validate loops integration and update skill files

* loops icon color

* update databricks icon
2026-02-28 12:37:01 -08:00
Waleed
40bab7731a fix(chat-deploy): fix launch chat popup and auth persistence, clean up React anti-patterns (#3380)
* fix(chat-deploy): fix launch chat popup and auth persistence, clean up React anti-patterns

* lint

* fix(greenhouse): fix email_address query param, add .trim() to ID paths, revert onValidationChange to useEffect

* fix(chat-deploy): fix stale AuthSelector state, stabilize refetch ref, clean up copy timeout

* fix(chat-deploy): reset chatSuccess on modal open to prevent stuck state
2026-02-28 12:01:42 -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
27 changed files with 448 additions and 477 deletions

View File

@@ -3998,7 +3998,7 @@ export function LoopsIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg {...props} viewBox='0 0 256 256' fill='none' xmlns='http://www.w3.org/2000/svg'>
<path
fill='currentColor'
fill='#FD4E00'
d='M192.352 88.042c0-7.012-5.685-12.697-12.697-12.697s-12.697 5.685-12.697 12.697c0 .634.052 1.255.142 1.866a25.248 25.248 0 0 0-4.9-.49c-14.006 0-25.36 11.354-25.36 25.36 0 1.63.16 3.222.456 4.765a37.8 37.8 0 0 0-9.296-1.173c-20.95 0-37.935 16.985-37.935 37.935S107.05 194.24 128 194.24s37.935-16.985 37.935-37.935a37.7 37.7 0 0 0-3.78-16.555 25.2 25.2 0 0 0 12.487-3.336 25.2 25.2 0 0 0 4.558 3.336v.02c14.006 0 25.36-11.354 25.36-25.36 0-12.48-9.018-22.855-20.888-24.996a12.6 12.6 0 0 0 8.68-11.972m-77.05 68.263c0-7.012 5.685-12.697 12.697-12.697s12.697 5.685 12.697 12.697c0 7.013-5.685 12.697-12.697 12.697s-12.697-5.685-12.697-12.697'
/>
</svg>
@@ -4542,7 +4542,7 @@ export function DatabricksIcon(props: SVGProps<SVGSVGElement>) {
<svg {...props} viewBox='0 0 241 266' fill='none' xmlns='http://www.w3.org/2000/svg'>
<path
d='M228.085 109.654L120.615 171.674L5.53493 105.41L0 108.475V156.582L120.615 225.911L228.085 164.128V189.596L120.615 251.615L5.53493 185.351L0 188.417V196.67L120.615 266L241 196.67V148.564L235.465 145.498L120.615 211.527L12.9148 149.743V124.275L120.615 186.059L241 116.729V69.3298L235.004 65.7925L120.615 131.585L18.4498 73.1028L120.615 14.3848L204.562 62.7269L211.942 58.4823V52.5869L120.615 0L0 69.3298V76.8759L120.615 146.206L228.085 84.1862V109.654Z'
fill='#F9F7F4'
fill='#FF3621'
/>
</svg>
)

View File

@@ -7,7 +7,7 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
<BlockInfoCard
type="databricks"
color="#FF3621"
color="#F9F7F4"
/>
{/* MANUAL-CONTENT-START:intro */}

View File

@@ -35,541 +35,472 @@ Integrate Greenhouse into the workflow. List and retrieve candidates, jobs, appl
### `greenhouse_list_candidates`
Lists candidates from Greenhouse with optional filtering by date, job, or email
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Greenhouse Harvest API key |
| `per_page` | number | No | Number of results per page \(1-500, default 100\) |
| `page` | number | No | Page number for pagination |
| `created_after` | string | No | Return only candidates created at or after this ISO 8601 timestamp |
| `created_before` | string | No | Return only candidates created before this ISO 8601 timestamp |
| `updated_after` | string | No | Return only candidates updated at or after this ISO 8601 timestamp |
| `updated_before` | string | No | Return only candidates updated before this ISO 8601 timestamp |
| `job_id` | string | No | Filter to candidates who applied to this job ID \(excludes prospects\) |
| `email` | string | No | Filter to candidates with this email address |
| `candidate_ids` | string | No | Comma-separated candidate IDs to retrieve \(max 50\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `candidates` | json | List of candidates |
| `jobs` | json | List of jobs |
| `applications` | json | List of applications |
| `users` | json | List of users |
| `departments` | json | List of departments |
| `offices` | json | List of offices |
| `stages` | json | List of job stages |
| `count` | number | Number of results returned |
| `id` | number | Resource ID |
| `first_name` | string | First name |
| `last_name` | string | Last name |
| `name` | string | Resource name |
| `status` | string | Status |
| `email_addresses` | json | Email addresses |
| `phone_numbers` | json | Phone numbers |
| `tags` | json | Tags |
| `application_ids` | json | Associated application IDs |
| `recruiter` | json | Assigned recruiter |
| `coordinator` | json | Assigned coordinator |
| `current_stage` | json | Current interview stage |
| `source` | json | Application source |
| `hiring_team` | json | Hiring team members |
| `openings` | json | Job openings |
| `custom_fields` | json | Custom field values |
| `attachments` | json | File attachments |
| `educations` | json | Education history |
| `employments` | json | Employment history |
| `answers` | json | Application question answers |
| `prospect` | boolean | Whether this is a prospect |
| `confidential` | boolean | Whether the job is confidential |
| `is_private` | boolean | Whether the candidate is private |
| `can_email` | boolean | Whether the candidate can be emailed |
| `disabled` | boolean | Whether the user is disabled |
| `site_admin` | boolean | Whether the user is a site admin |
| `primary_email_address` | string | Primary email address |
| `created_at` | string | Creation timestamp \(ISO 8601\) |
| `updated_at` | string | Last updated timestamp \(ISO 8601\) |
| `candidates` | array | List of candidates |
| ↳ `id` | number | Candidate ID |
| ↳ `first_name` | string | First name |
| ↳ `last_name` | string | Last name |
| ↳ `company` | string | Current employer |
| ↳ `title` | string | Current job title |
| ↳ `is_private` | boolean | Whether candidate is private |
| ↳ `can_email` | boolean | Whether candidate can be emailed |
| ↳ `email_addresses` | array | Email addresses |
| ↳ `value` | string | Email address |
| ↳ `type` | string | Email type \(personal, work, other\) |
| ↳ `tags` | array | Candidate tags |
| ↳ `application_ids` | array | Associated application IDs |
| ↳ `created_at` | string | Creation timestamp \(ISO 8601\) |
| ↳ `updated_at` | string | Last updated timestamp \(ISO 8601\) |
| ↳ `last_activity` | string | Last activity timestamp \(ISO 8601\) |
| `count` | number | Number of candidates returned |
### `greenhouse_get_candidate`
Retrieves a specific candidate by ID with full details including contact info, education, and employment history
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Greenhouse Harvest API key |
| `candidateId` | string | Yes | The ID of the candidate to retrieve |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `candidates` | json | List of candidates |
| `jobs` | json | List of jobs |
| `applications` | json | List of applications |
| `users` | json | List of users |
| `departments` | json | List of departments |
| `offices` | json | List of offices |
| `stages` | json | List of job stages |
| `count` | number | Number of results returned |
| `id` | number | Resource ID |
| `id` | number | Candidate ID |
| `first_name` | string | First name |
| `last_name` | string | Last name |
| `name` | string | Resource name |
| `status` | string | Status |
| `email_addresses` | json | Email addresses |
| `phone_numbers` | json | Phone numbers |
| `tags` | json | Tags |
| `application_ids` | json | Associated application IDs |
| `recruiter` | json | Assigned recruiter |
| `coordinator` | json | Assigned coordinator |
| `current_stage` | json | Current interview stage |
| `source` | json | Application source |
| `hiring_team` | json | Hiring team members |
| `openings` | json | Job openings |
| `custom_fields` | json | Custom field values |
| `attachments` | json | File attachments |
| `educations` | json | Education history |
| `employments` | json | Employment history |
| `answers` | json | Application question answers |
| `prospect` | boolean | Whether this is a prospect |
| `confidential` | boolean | Whether the job is confidential |
| `is_private` | boolean | Whether the candidate is private |
| `can_email` | boolean | Whether the candidate can be emailed |
| `disabled` | boolean | Whether the user is disabled |
| `site_admin` | boolean | Whether the user is a site admin |
| `primary_email_address` | string | Primary email address |
| `company` | string | Current employer |
| `title` | string | Current job title |
| `is_private` | boolean | Whether candidate is private |
| `can_email` | boolean | Whether candidate can be emailed |
| `created_at` | string | Creation timestamp \(ISO 8601\) |
| `updated_at` | string | Last updated timestamp \(ISO 8601\) |
| `last_activity` | string | Last activity timestamp \(ISO 8601\) |
| `email_addresses` | array | Email addresses |
| ↳ `value` | string | Email address |
| ↳ `type` | string | Type \(personal, work, other\) |
| `phone_numbers` | array | Phone numbers |
| ↳ `value` | string | Phone number |
| ↳ `type` | string | Type \(home, work, mobile, skype, other\) |
| `addresses` | array | Addresses |
| ↳ `value` | string | Address |
| ↳ `type` | string | Type \(home, work, other\) |
| `website_addresses` | array | Website addresses |
| ↳ `value` | string | URL |
| ↳ `type` | string | Type \(personal, company, portfolio, blog, other\) |
| `social_media_addresses` | array | Social media profiles |
| ↳ `value` | string | URL or handle |
| `tags` | array | Tags |
| `application_ids` | array | Associated application IDs |
| `recruiter` | object | Assigned recruiter |
| ↳ `id` | number | User ID |
| ↳ `first_name` | string | First name |
| ↳ `last_name` | string | Last name |
| ↳ `name` | string | Full name |
| ↳ `employee_id` | string | Employee ID |
| `coordinator` | object | Assigned coordinator |
| ↳ `id` | number | User ID |
| ↳ `first_name` | string | First name |
| ↳ `last_name` | string | Last name |
| ↳ `name` | string | Full name |
| ↳ `employee_id` | string | Employee ID |
| `attachments` | array | File attachments \(URLs expire after 7 days\) |
| ↳ `filename` | string | File name |
| ↳ `url` | string | Download URL \(expires after 7 days\) |
| ↳ `type` | string | Type \(resume, cover_letter, offer_packet, other\) |
| ↳ `created_at` | string | Upload timestamp |
| `educations` | array | Education history |
| ↳ `id` | number | Education record ID |
| ↳ `school_name` | string | School name |
| ↳ `degree` | string | Degree type |
| ↳ `discipline` | string | Field of study |
| ↳ `start_date` | string | Start date \(ISO 8601\) |
| ↳ `end_date` | string | End date \(ISO 8601\) |
| `employments` | array | Employment history |
| ↳ `id` | number | Employment record ID |
| ↳ `company_name` | string | Company name |
| ↳ `title` | string | Job title |
| ↳ `start_date` | string | Start date \(ISO 8601\) |
| ↳ `end_date` | string | End date \(ISO 8601\) |
| `custom_fields` | object | Custom field values |
### `greenhouse_list_jobs`
Lists jobs from Greenhouse with optional filtering by status, department, or office
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Greenhouse Harvest API key |
| `per_page` | number | No | Number of results per page \(1-500, default 100\) |
| `page` | number | No | Page number for pagination |
| `status` | string | No | Filter by job status \(open, closed, draft\) |
| `created_after` | string | No | Return only jobs created at or after this ISO 8601 timestamp |
| `created_before` | string | No | Return only jobs created before this ISO 8601 timestamp |
| `updated_after` | string | No | Return only jobs updated at or after this ISO 8601 timestamp |
| `updated_before` | string | No | Return only jobs updated before this ISO 8601 timestamp |
| `department_id` | string | No | Filter to jobs in this department ID |
| `office_id` | string | No | Filter to jobs in this office ID |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `candidates` | json | List of candidates |
| `jobs` | json | List of jobs |
| `applications` | json | List of applications |
| `users` | json | List of users |
| `departments` | json | List of departments |
| `offices` | json | List of offices |
| `stages` | json | List of job stages |
| `count` | number | Number of results returned |
| `id` | number | Resource ID |
| `first_name` | string | First name |
| `last_name` | string | Last name |
| `name` | string | Resource name |
| `status` | string | Status |
| `email_addresses` | json | Email addresses |
| `phone_numbers` | json | Phone numbers |
| `tags` | json | Tags |
| `application_ids` | json | Associated application IDs |
| `recruiter` | json | Assigned recruiter |
| `coordinator` | json | Assigned coordinator |
| `current_stage` | json | Current interview stage |
| `source` | json | Application source |
| `hiring_team` | json | Hiring team members |
| `openings` | json | Job openings |
| `custom_fields` | json | Custom field values |
| `attachments` | json | File attachments |
| `educations` | json | Education history |
| `employments` | json | Employment history |
| `answers` | json | Application question answers |
| `prospect` | boolean | Whether this is a prospect |
| `confidential` | boolean | Whether the job is confidential |
| `is_private` | boolean | Whether the candidate is private |
| `can_email` | boolean | Whether the candidate can be emailed |
| `disabled` | boolean | Whether the user is disabled |
| `site_admin` | boolean | Whether the user is a site admin |
| `primary_email_address` | string | Primary email address |
| `created_at` | string | Creation timestamp \(ISO 8601\) |
| `updated_at` | string | Last updated timestamp \(ISO 8601\) |
| `jobs` | array | List of jobs |
| ↳ `id` | number | Job ID |
| ↳ `name` | string | Job title |
| ↳ `status` | string | Job status \(open, closed, draft\) |
| ↳ `confidential` | boolean | Whether the job is confidential |
| ↳ `departments` | array | Associated departments |
| ↳ `id` | number | Department ID |
| ↳ `name` | string | Department name |
| ↳ `offices` | array | Associated offices |
| ↳ `id` | number | Office ID |
| ↳ `name` | string | Office name |
| ↳ `opened_at` | string | Date job was opened \(ISO 8601\) |
| ↳ `closed_at` | string | Date job was closed \(ISO 8601\) |
| ↳ `created_at` | string | Creation timestamp \(ISO 8601\) |
| ↳ `updated_at` | string | Last updated timestamp \(ISO 8601\) |
| `count` | number | Number of jobs returned |
### `greenhouse_get_job`
Retrieves a specific job by ID with full details including hiring team, openings, and custom fields
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Greenhouse Harvest API key |
| `jobId` | string | Yes | The ID of the job to retrieve |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `candidates` | json | List of candidates |
| `jobs` | json | List of jobs |
| `applications` | json | List of applications |
| `users` | json | List of users |
| `departments` | json | List of departments |
| `offices` | json | List of offices |
| `stages` | json | List of job stages |
| `count` | number | Number of results returned |
| `id` | number | Resource ID |
| `first_name` | string | First name |
| `last_name` | string | Last name |
| `name` | string | Resource name |
| `status` | string | Status |
| `email_addresses` | json | Email addresses |
| `phone_numbers` | json | Phone numbers |
| `tags` | json | Tags |
| `application_ids` | json | Associated application IDs |
| `recruiter` | json | Assigned recruiter |
| `coordinator` | json | Assigned coordinator |
| `current_stage` | json | Current interview stage |
| `source` | json | Application source |
| `hiring_team` | json | Hiring team members |
| `openings` | json | Job openings |
| `custom_fields` | json | Custom field values |
| `attachments` | json | File attachments |
| `educations` | json | Education history |
| `employments` | json | Employment history |
| `answers` | json | Application question answers |
| `prospect` | boolean | Whether this is a prospect |
| `id` | number | Job ID |
| `name` | string | Job title |
| `requisition_id` | string | External requisition ID |
| `status` | string | Job status \(open, closed, draft\) |
| `confidential` | boolean | Whether the job is confidential |
| `is_private` | boolean | Whether the candidate is private |
| `can_email` | boolean | Whether the candidate can be emailed |
| `disabled` | boolean | Whether the user is disabled |
| `site_admin` | boolean | Whether the user is a site admin |
| `primary_email_address` | string | Primary email address |
| `created_at` | string | Creation timestamp \(ISO 8601\) |
| `opened_at` | string | Date job was opened \(ISO 8601\) |
| `closed_at` | string | Date job was closed \(ISO 8601\) |
| `updated_at` | string | Last updated timestamp \(ISO 8601\) |
| `is_template` | boolean | Whether this is a job template |
| `notes` | string | Hiring plan notes \(may contain HTML\) |
| `departments` | array | Associated departments |
| ↳ `id` | number | Department ID |
| ↳ `name` | string | Department name |
| ↳ `parent_id` | number | Parent department ID |
| `offices` | array | Associated offices |
| ↳ `id` | number | Office ID |
| ↳ `name` | string | Office name |
| ↳ `location` | object | Office location |
| ↳ `name` | string | Location name |
| `hiring_team` | object | Hiring team members |
| ↳ `hiring_managers` | array | Hiring managers |
| ↳ `recruiters` | array | Recruiters \(includes responsible flag\) |
| ↳ `coordinators` | array | Coordinators \(includes responsible flag\) |
| ↳ `sourcers` | array | Sourcers |
| `openings` | array | Job openings/slots |
| ↳ `id` | number | Opening internal ID |
| ↳ `opening_id` | string | Custom opening identifier |
| ↳ `status` | string | Opening status \(open, closed\) |
| ↳ `opened_at` | string | Date opened \(ISO 8601\) |
| ↳ `closed_at` | string | Date closed \(ISO 8601\) |
| ↳ `application_id` | number | Hired application ID |
| ↳ `close_reason` | object | Reason for closing |
| ↳ `id` | number | Close reason ID |
| ↳ `name` | string | Close reason name |
| `custom_fields` | object | Custom field values |
### `greenhouse_list_applications`
Lists applications from Greenhouse with optional filtering by job, status, or date
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Greenhouse Harvest API key |
| `per_page` | number | No | Number of results per page \(1-500, default 100\) |
| `page` | number | No | Page number for pagination |
| `job_id` | string | No | Filter applications by job ID |
| `status` | string | No | Filter by status \(active, converted, hired, rejected\) |
| `created_after` | string | No | Return only applications created at or after this ISO 8601 timestamp |
| `created_before` | string | No | Return only applications created before this ISO 8601 timestamp |
| `last_activity_after` | string | No | Return only applications with activity at or after this ISO 8601 timestamp |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `candidates` | json | List of candidates |
| `jobs` | json | List of jobs |
| `applications` | json | List of applications |
| `users` | json | List of users |
| `departments` | json | List of departments |
| `offices` | json | List of offices |
| `stages` | json | List of job stages |
| `count` | number | Number of results returned |
| `id` | number | Resource ID |
| `first_name` | string | First name |
| `last_name` | string | Last name |
| `name` | string | Resource name |
| `status` | string | Status |
| `email_addresses` | json | Email addresses |
| `phone_numbers` | json | Phone numbers |
| `tags` | json | Tags |
| `application_ids` | json | Associated application IDs |
| `recruiter` | json | Assigned recruiter |
| `coordinator` | json | Assigned coordinator |
| `current_stage` | json | Current interview stage |
| `source` | json | Application source |
| `hiring_team` | json | Hiring team members |
| `openings` | json | Job openings |
| `custom_fields` | json | Custom field values |
| `attachments` | json | File attachments |
| `educations` | json | Education history |
| `employments` | json | Employment history |
| `answers` | json | Application question answers |
| `prospect` | boolean | Whether this is a prospect |
| `confidential` | boolean | Whether the job is confidential |
| `is_private` | boolean | Whether the candidate is private |
| `can_email` | boolean | Whether the candidate can be emailed |
| `disabled` | boolean | Whether the user is disabled |
| `site_admin` | boolean | Whether the user is a site admin |
| `primary_email_address` | string | Primary email address |
| `created_at` | string | Creation timestamp \(ISO 8601\) |
| `updated_at` | string | Last updated timestamp \(ISO 8601\) |
| `applications` | array | List of applications |
| ↳ `id` | number | Application ID |
| ↳ `candidate_id` | number | Associated candidate ID |
| ↳ `prospect` | boolean | Whether this is a prospect application |
| ↳ `status` | string | Status \(active, converted, hired, rejected\) |
| ↳ `current_stage` | object | Current interview stage |
| ↳ `id` | number | Stage ID |
| ↳ `name` | string | Stage name |
| ↳ `jobs` | array | Associated jobs |
| ↳ `id` | number | Job ID |
| ↳ `name` | string | Job name |
| ↳ `applied_at` | string | Application date \(ISO 8601\) |
| ↳ `rejected_at` | string | Rejection date \(ISO 8601\) |
| ↳ `last_activity_at` | string | Last activity date \(ISO 8601\) |
| `count` | number | Number of applications returned |
### `greenhouse_get_application`
Retrieves a specific application by ID with full details including source, stage, answers, and attachments
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Greenhouse Harvest API key |
| `applicationId` | string | Yes | The ID of the application to retrieve |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `candidates` | json | List of candidates |
| `jobs` | json | List of jobs |
| `applications` | json | List of applications |
| `users` | json | List of users |
| `departments` | json | List of departments |
| `offices` | json | List of offices |
| `stages` | json | List of job stages |
| `count` | number | Number of results returned |
| `id` | number | Resource ID |
| `first_name` | string | First name |
| `last_name` | string | Last name |
| `name` | string | Resource name |
| `status` | string | Status |
| `email_addresses` | json | Email addresses |
| `phone_numbers` | json | Phone numbers |
| `tags` | json | Tags |
| `application_ids` | json | Associated application IDs |
| `recruiter` | json | Assigned recruiter |
| `coordinator` | json | Assigned coordinator |
| `current_stage` | json | Current interview stage |
| `source` | json | Application source |
| `hiring_team` | json | Hiring team members |
| `openings` | json | Job openings |
| `custom_fields` | json | Custom field values |
| `attachments` | json | File attachments |
| `educations` | json | Education history |
| `employments` | json | Employment history |
| `answers` | json | Application question answers |
| `prospect` | boolean | Whether this is a prospect |
| `confidential` | boolean | Whether the job is confidential |
| `is_private` | boolean | Whether the candidate is private |
| `can_email` | boolean | Whether the candidate can be emailed |
| `disabled` | boolean | Whether the user is disabled |
| `site_admin` | boolean | Whether the user is a site admin |
| `primary_email_address` | string | Primary email address |
| `created_at` | string | Creation timestamp \(ISO 8601\) |
| `updated_at` | string | Last updated timestamp \(ISO 8601\) |
| `id` | number | Application ID |
| `candidate_id` | number | Associated candidate ID |
| `prospect` | boolean | Whether this is a prospect application |
| `status` | string | Status \(active, converted, hired, rejected\) |
| `applied_at` | string | Application date \(ISO 8601\) |
| `rejected_at` | string | Rejection date \(ISO 8601\) |
| `last_activity_at` | string | Last activity date \(ISO 8601\) |
| `location` | object | Candidate location |
| ↳ `address` | string | Location address |
| `source` | object | Application source |
| ↳ `id` | number | Source ID |
| ↳ `public_name` | string | Source name |
| `credited_to` | object | User credited for the application |
| ↳ `id` | number | User ID |
| ↳ `first_name` | string | First name |
| ↳ `last_name` | string | Last name |
| ↳ `name` | string | Full name |
| ↳ `employee_id` | string | Employee ID |
| `recruiter` | object | Assigned recruiter |
| ↳ `id` | number | User ID |
| ↳ `first_name` | string | First name |
| ↳ `last_name` | string | Last name |
| ↳ `name` | string | Full name |
| ↳ `employee_id` | string | Employee ID |
| `coordinator` | object | Assigned coordinator |
| ↳ `id` | number | User ID |
| ↳ `first_name` | string | First name |
| ↳ `last_name` | string | Last name |
| ↳ `name` | string | Full name |
| ↳ `employee_id` | string | Employee ID |
| `current_stage` | object | Current interview stage \(null when hired\) |
| ↳ `id` | number | Stage ID |
| ↳ `name` | string | Stage name |
| `rejection_reason` | object | Rejection reason |
| ↳ `id` | number | Rejection reason ID |
| ↳ `name` | string | Rejection reason name |
| ↳ `type` | object | Rejection reason type |
| ↳ `id` | number | Type ID |
| ↳ `name` | string | Type name |
| `jobs` | array | Associated jobs |
| ↳ `id` | number | Job ID |
| ↳ `name` | string | Job name |
| `job_post_id` | number | Job post ID |
| `answers` | array | Application question answers |
| ↳ `question` | string | Question text |
| ↳ `answer` | string | Answer text |
| `attachments` | array | File attachments \(URLs expire after 7 days\) |
| ↳ `filename` | string | File name |
| ↳ `url` | string | Download URL \(expires after 7 days\) |
| ↳ `type` | string | Type \(resume, cover_letter, offer_packet, other\) |
| ↳ `created_at` | string | Upload timestamp |
| `custom_fields` | object | Custom field values |
### `greenhouse_list_users`
Lists Greenhouse users (recruiters, hiring managers, admins) with optional filtering
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Greenhouse Harvest API key |
| `per_page` | number | No | Number of results per page \(1-500, default 100\) |
| `page` | number | No | Page number for pagination |
| `created_after` | string | No | Return only users created at or after this ISO 8601 timestamp |
| `created_before` | string | No | Return only users created before this ISO 8601 timestamp |
| `updated_after` | string | No | Return only users updated at or after this ISO 8601 timestamp |
| `updated_before` | string | No | Return only users updated before this ISO 8601 timestamp |
| `email` | string | No | Filter by email address |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `candidates` | json | List of candidates |
| `jobs` | json | List of jobs |
| `applications` | json | List of applications |
| `users` | json | List of users |
| `departments` | json | List of departments |
| `offices` | json | List of offices |
| `stages` | json | List of job stages |
| `count` | number | Number of results returned |
| `id` | number | Resource ID |
| `first_name` | string | First name |
| `last_name` | string | Last name |
| `name` | string | Resource name |
| `status` | string | Status |
| `email_addresses` | json | Email addresses |
| `phone_numbers` | json | Phone numbers |
| `tags` | json | Tags |
| `application_ids` | json | Associated application IDs |
| `recruiter` | json | Assigned recruiter |
| `coordinator` | json | Assigned coordinator |
| `current_stage` | json | Current interview stage |
| `source` | json | Application source |
| `hiring_team` | json | Hiring team members |
| `openings` | json | Job openings |
| `custom_fields` | json | Custom field values |
| `attachments` | json | File attachments |
| `educations` | json | Education history |
| `employments` | json | Employment history |
| `answers` | json | Application question answers |
| `prospect` | boolean | Whether this is a prospect |
| `confidential` | boolean | Whether the job is confidential |
| `is_private` | boolean | Whether the candidate is private |
| `can_email` | boolean | Whether the candidate can be emailed |
| `disabled` | boolean | Whether the user is disabled |
| `site_admin` | boolean | Whether the user is a site admin |
| `primary_email_address` | string | Primary email address |
| `created_at` | string | Creation timestamp \(ISO 8601\) |
| `updated_at` | string | Last updated timestamp \(ISO 8601\) |
| `users` | array | List of Greenhouse users |
| ↳ `id` | number | User ID |
| ↳ `name` | string | Full name |
| ↳ `first_name` | string | First name |
| ↳ `last_name` | string | Last name |
| ↳ `primary_email_address` | string | Primary email |
| ↳ `disabled` | boolean | Whether the user is disabled |
| ↳ `site_admin` | boolean | Whether the user is a site admin |
| ↳ `emails` | array | All email addresses |
| ↳ `employee_id` | string | Employee ID |
| ↳ `linked_candidate_ids` | array | IDs of candidates linked to this user |
| ↳ `created_at` | string | Creation timestamp \(ISO 8601\) |
| ↳ `updated_at` | string | Last updated timestamp \(ISO 8601\) |
| `count` | number | Number of users returned |
### `greenhouse_get_user`
Retrieves a specific Greenhouse user by ID
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Greenhouse Harvest API key |
| `userId` | string | Yes | The ID of the user to retrieve |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `candidates` | json | List of candidates |
| `jobs` | json | List of jobs |
| `applications` | json | List of applications |
| `users` | json | List of users |
| `departments` | json | List of departments |
| `offices` | json | List of offices |
| `stages` | json | List of job stages |
| `count` | number | Number of results returned |
| `id` | number | Resource ID |
| `id` | number | User ID |
| `name` | string | Full name |
| `first_name` | string | First name |
| `last_name` | string | Last name |
| `name` | string | Resource name |
| `status` | string | Status |
| `email_addresses` | json | Email addresses |
| `phone_numbers` | json | Phone numbers |
| `tags` | json | Tags |
| `application_ids` | json | Associated application IDs |
| `recruiter` | json | Assigned recruiter |
| `coordinator` | json | Assigned coordinator |
| `current_stage` | json | Current interview stage |
| `source` | json | Application source |
| `hiring_team` | json | Hiring team members |
| `openings` | json | Job openings |
| `custom_fields` | json | Custom field values |
| `attachments` | json | File attachments |
| `educations` | json | Education history |
| `employments` | json | Employment history |
| `answers` | json | Application question answers |
| `prospect` | boolean | Whether this is a prospect |
| `confidential` | boolean | Whether the job is confidential |
| `is_private` | boolean | Whether the candidate is private |
| `can_email` | boolean | Whether the candidate can be emailed |
| `primary_email_address` | string | Primary email address |
| `disabled` | boolean | Whether the user is disabled |
| `site_admin` | boolean | Whether the user is a site admin |
| `primary_email_address` | string | Primary email address |
| `emails` | array | All email addresses |
| `employee_id` | string | Employee ID |
| `linked_candidate_ids` | array | IDs of candidates linked to this user |
| `created_at` | string | Creation timestamp \(ISO 8601\) |
| `updated_at` | string | Last updated timestamp \(ISO 8601\) |
### `greenhouse_list_departments`
Lists all departments configured in Greenhouse
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Greenhouse Harvest API key |
| `per_page` | number | No | Number of results per page \(1-500, default 100\) |
| `page` | number | No | Page number for pagination |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `candidates` | json | List of candidates |
| `jobs` | json | List of jobs |
| `applications` | json | List of applications |
| `users` | json | List of users |
| `departments` | json | List of departments |
| `offices` | json | List of offices |
| `stages` | json | List of job stages |
| `count` | number | Number of results returned |
| `id` | number | Resource ID |
| `first_name` | string | First name |
| `last_name` | string | Last name |
| `name` | string | Resource name |
| `status` | string | Status |
| `email_addresses` | json | Email addresses |
| `phone_numbers` | json | Phone numbers |
| `tags` | json | Tags |
| `application_ids` | json | Associated application IDs |
| `recruiter` | json | Assigned recruiter |
| `coordinator` | json | Assigned coordinator |
| `current_stage` | json | Current interview stage |
| `source` | json | Application source |
| `hiring_team` | json | Hiring team members |
| `openings` | json | Job openings |
| `custom_fields` | json | Custom field values |
| `attachments` | json | File attachments |
| `educations` | json | Education history |
| `employments` | json | Employment history |
| `answers` | json | Application question answers |
| `prospect` | boolean | Whether this is a prospect |
| `confidential` | boolean | Whether the job is confidential |
| `is_private` | boolean | Whether the candidate is private |
| `can_email` | boolean | Whether the candidate can be emailed |
| `disabled` | boolean | Whether the user is disabled |
| `site_admin` | boolean | Whether the user is a site admin |
| `primary_email_address` | string | Primary email address |
| `created_at` | string | Creation timestamp \(ISO 8601\) |
| `updated_at` | string | Last updated timestamp \(ISO 8601\) |
| `departments` | array | List of departments |
| ↳ `id` | number | Department ID |
| ↳ `name` | string | Department name |
| ↳ `parent_id` | number | Parent department ID |
| ↳ `child_ids` | array | Child department IDs |
| ↳ `external_id` | string | External system ID |
| `count` | number | Number of departments returned |
### `greenhouse_list_offices`
Lists all offices configured in Greenhouse
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Greenhouse Harvest API key |
| `per_page` | number | No | Number of results per page \(1-500, default 100\) |
| `page` | number | No | Page number for pagination |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `candidates` | json | List of candidates |
| `jobs` | json | List of jobs |
| `applications` | json | List of applications |
| `users` | json | List of users |
| `departments` | json | List of departments |
| `offices` | json | List of offices |
| `stages` | json | List of job stages |
| `count` | number | Number of results returned |
| `id` | number | Resource ID |
| `first_name` | string | First name |
| `last_name` | string | Last name |
| `name` | string | Resource name |
| `status` | string | Status |
| `email_addresses` | json | Email addresses |
| `phone_numbers` | json | Phone numbers |
| `tags` | json | Tags |
| `application_ids` | json | Associated application IDs |
| `recruiter` | json | Assigned recruiter |
| `coordinator` | json | Assigned coordinator |
| `current_stage` | json | Current interview stage |
| `source` | json | Application source |
| `hiring_team` | json | Hiring team members |
| `openings` | json | Job openings |
| `custom_fields` | json | Custom field values |
| `attachments` | json | File attachments |
| `educations` | json | Education history |
| `employments` | json | Employment history |
| `answers` | json | Application question answers |
| `prospect` | boolean | Whether this is a prospect |
| `confidential` | boolean | Whether the job is confidential |
| `is_private` | boolean | Whether the candidate is private |
| `can_email` | boolean | Whether the candidate can be emailed |
| `disabled` | boolean | Whether the user is disabled |
| `site_admin` | boolean | Whether the user is a site admin |
| `primary_email_address` | string | Primary email address |
| `created_at` | string | Creation timestamp \(ISO 8601\) |
| `updated_at` | string | Last updated timestamp \(ISO 8601\) |
| `offices` | array | List of offices |
| ↳ `id` | number | Office ID |
| ↳ `name` | string | Office name |
| ↳ `location` | object | Office location |
| ↳ `name` | string | Location name |
| ↳ `primary_contact_user_id` | number | Primary contact user ID |
| ↳ `parent_id` | number | Parent office ID |
| ↳ `child_ids` | array | Child office IDs |
| ↳ `external_id` | string | External system ID |
| `count` | number | Number of offices returned |
### `greenhouse_list_job_stages`
Lists all interview stages for a specific job in Greenhouse
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Greenhouse Harvest API key |
| `jobId` | string | Yes | The job ID to list stages for |
| `per_page` | number | No | Number of results per page \(1-500, default 100\) |
| `page` | number | No | Page number for pagination |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `candidates` | json | List of candidates |
| `jobs` | json | List of jobs |
| `applications` | json | List of applications |
| `users` | json | List of users |
| `departments` | json | List of departments |
| `offices` | json | List of offices |
| `stages` | json | List of job stages |
| `count` | number | Number of results returned |
| `id` | number | Resource ID |
| `first_name` | string | First name |
| `last_name` | string | Last name |
| `name` | string | Resource name |
| `status` | string | Status |
| `email_addresses` | json | Email addresses |
| `phone_numbers` | json | Phone numbers |
| `tags` | json | Tags |
| `application_ids` | json | Associated application IDs |
| `recruiter` | json | Assigned recruiter |
| `coordinator` | json | Assigned coordinator |
| `current_stage` | json | Current interview stage |
| `source` | json | Application source |
| `hiring_team` | json | Hiring team members |
| `openings` | json | Job openings |
| `custom_fields` | json | Custom field values |
| `attachments` | json | File attachments |
| `educations` | json | Education history |
| `employments` | json | Employment history |
| `answers` | json | Application question answers |
| `prospect` | boolean | Whether this is a prospect |
| `confidential` | boolean | Whether the job is confidential |
| `is_private` | boolean | Whether the candidate is private |
| `can_email` | boolean | Whether the candidate can be emailed |
| `disabled` | boolean | Whether the user is disabled |
| `site_admin` | boolean | Whether the user is a site admin |
| `primary_email_address` | string | Primary email address |
| `created_at` | string | Creation timestamp \(ISO 8601\) |
| `updated_at` | string | Last updated timestamp \(ISO 8601\) |
| `stages` | array | List of job stages in order |
| ↳ `id` | number | Stage ID |
| ↳ `name` | string | Stage name |
| ↳ `created_at` | string | Creation timestamp \(ISO 8601\) |
| ↳ `updated_at` | string | Last updated timestamp \(ISO 8601\) |
| ↳ `job_id` | number | Associated job ID |
| ↳ `priority` | number | Stage order priority |
| ↳ `active` | boolean | Whether the stage is active |
| ↳ `interviews` | array | Interview steps in this stage |
| ↳ `id` | number | Interview ID |
| ↳ `name` | string | Interview name |
| ↳ `schedulable` | boolean | Whether the interview is schedulable |
| ↳ `estimated_minutes` | number | Estimated duration in minutes |
| ↳ `default_interviewer_users` | array | Default interviewers |
| ↳ `id` | number | User ID |
| ↳ `name` | string | Full name |
| ↳ `first_name` | string | First name |
| ↳ `last_name` | string | Last name |
| ↳ `employee_id` | string | Employee ID |
| ↳ `interview_kit` | object | Interview kit details |
| ↳ `id` | number | Kit ID |
| ↳ `content` | string | Kit content \(HTML\) |
| ↳ `questions` | array | Interview kit questions |
| ↳ `id` | number | Question ID |
| ↳ `question` | string | Question text |
| `count` | number | Number of stages returned |

View File

@@ -108,7 +108,6 @@ export function ChatDeploy({
onVersionActivated,
}: ChatDeployProps) {
const [imageUrl, setImageUrl] = useState<string | null>(null)
const [isDeleting, setIsDeleting] = useState(false)
const [internalShowDeleteConfirmation, setInternalShowDeleteConfirmation] = useState(false)
const showDeleteConfirmation =
@@ -122,6 +121,7 @@ export function ChatDeploy({
const [formData, setFormData] = useState<ChatFormData>(initialFormData)
const [errors, setErrors] = useState<FormErrors>({})
const formRef = useRef<HTMLFormElement>(null)
const [formInitCounter, setFormInitCounter] = useState(0)
const createChatMutation = useCreateChat()
const updateChatMutation = useUpdateChat()
@@ -222,13 +222,20 @@ export function ChatDeploy({
setChatSubmitting(true)
const isNewChat = !existingChat?.id
// Open window before async operation to avoid popup blockers
const newTab = isNewChat ? window.open('', '_blank') : null
try {
if (!validateForm(!!existingChat)) {
newTab?.close()
setChatSubmitting(false)
return
}
if (!isIdentifierValid && formData.identifier !== existingChat?.identifier) {
newTab?.close()
setError('identifier', 'Please wait for identifier validation to complete')
setChatSubmitting(false)
return
@@ -257,13 +264,18 @@ export function ChatDeploy({
onDeployed?.()
onVersionActivated?.()
if (chatUrl) {
window.open(chatUrl, '_blank', 'noopener,noreferrer')
if (newTab && chatUrl) {
newTab.opener = null
newTab.location.href = chatUrl
} else if (newTab) {
newTab.close()
}
setHasInitializedForm(false)
await onRefetchChat()
setHasInitializedForm(false)
setFormInitCounter((c) => c + 1)
} catch (error: any) {
newTab?.close()
if (error.message?.includes('identifier')) {
setError('identifier', error.message)
} else {
@@ -278,8 +290,6 @@ export function ChatDeploy({
if (!existingChat || !existingChat.id) return
try {
setIsDeleting(true)
await deleteChatMutation.mutateAsync({
chatId: existingChat.id,
workflowId,
@@ -287,6 +297,7 @@ export function ChatDeploy({
setImageUrl(null)
setHasInitializedForm(false)
setFormInitCounter((c) => c + 1)
await onRefetchChat()
onDeploymentComplete?.()
@@ -294,7 +305,6 @@ export function ChatDeploy({
logger.error('Failed to delete chat:', error)
setError('general', error.message || 'An unexpected error occurred while deleting')
} finally {
setIsDeleting(false)
setShowDeleteConfirmation(false)
}
}
@@ -363,7 +373,7 @@ export function ChatDeploy({
</div>
<AuthSelector
key={existingChat?.id ?? 'new'}
key={`${existingChat?.id ?? 'new'}-${formInitCounter}`}
authType={formData.authType}
password={formData.password}
emails={formData.emails}
@@ -424,12 +434,16 @@ export function ChatDeploy({
<Button
variant='default'
onClick={() => setShowDeleteConfirmation(false)}
disabled={isDeleting}
disabled={deleteChatMutation.isPending}
>
Cancel
</Button>
<Button variant='destructive' onClick={handleDelete} disabled={isDeleting}>
{isDeleting ? 'Deleting...' : 'Delete'}
<Button
variant='destructive'
onClick={handleDelete}
disabled={deleteChatMutation.isPending}
>
{deleteChatMutation.isPending ? 'Deleting...' : 'Delete'}
</Button>
</ModalFooter>
</ModalContent>
@@ -620,6 +634,12 @@ function AuthSelector({
emails.map((email) => ({ value: email, isValid: true }))
)
useEffect(() => {
if (!copySuccess) return
const timer = setTimeout(() => setCopySuccess(false), 2000)
return () => clearTimeout(timer)
}, [copySuccess])
const handleGeneratePassword = () => {
const newPassword = generatePassword(24)
onPasswordChange(newPassword)
@@ -628,7 +648,6 @@ function AuthSelector({
const copyToClipboard = (text: string) => {
navigator.clipboard.writeText(text)
setCopySuccess(true)
setTimeout(() => setCopySuccess(false), 2000)
}
const addEmail = (email: string): boolean => {

View File

@@ -1,6 +1,6 @@
'use client'
import { useCallback, useEffect, useMemo, useState } from 'react'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { createLogger } from '@sim/logger'
import { useQueryClient } from '@tanstack/react-query'
import {
@@ -113,6 +113,7 @@ export function DeployModal({
const [showA2aDeleteConfirm, setShowA2aDeleteConfirm] = useState(false)
const [chatSuccess, setChatSuccess] = useState(false)
const chatSuccessTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)
const [isCreateKeyModalOpen, setIsCreateKeyModalOpen] = useState(false)
const [isApiInfoModalOpen, setIsApiInfoModalOpen] = useState(false)
@@ -232,6 +233,12 @@ export function DeployModal({
setActiveTab('general')
setDeployError(null)
setDeployWarnings([])
setChatSuccess(false)
}
return () => {
if (chatSuccessTimeoutRef.current) {
clearTimeout(chatSuccessTimeoutRef.current)
}
}
}, [open, workflowId])
@@ -377,15 +384,16 @@ export function DeployModal({
const handleChatDeployed = useCallback(async () => {
if (!workflowId) return
queryClient.invalidateQueries({ queryKey: deploymentKeys.info(workflowId) })
queryClient.invalidateQueries({ queryKey: deploymentKeys.versions(workflowId) })
queryClient.invalidateQueries({ queryKey: deploymentKeys.chatStatus(workflowId) })
await refetchDeployedState()
useWorkflowRegistry.getState().setWorkflowNeedsRedeployment(workflowId, false)
if (chatSuccessTimeoutRef.current) {
clearTimeout(chatSuccessTimeoutRef.current)
}
setChatSuccess(true)
setTimeout(() => setChatSuccess(false), 2000)
chatSuccessTimeoutRef.current = setTimeout(() => setChatSuccess(false), 2000)
}, [workflowId, queryClient, refetchDeployedState])
const handleRefetchChat = useCallback(async () => {
@@ -394,14 +402,7 @@ export function DeployModal({
const handleChatFormSubmit = useCallback(() => {
const form = document.getElementById('chat-deploy-form') as HTMLFormElement
if (form) {
const updateTrigger = form.querySelector('[data-update-trigger]') as HTMLButtonElement
if (updateTrigger) {
updateTrigger.click()
} else {
form.requestSubmit()
}
}
form?.requestSubmit()
}, [])
const handleChatDelete = useCallback(() => {

View File

@@ -12,7 +12,7 @@ export const DatabricksBlock: BlockConfig<DatabricksResponse> = {
'Connect to Databricks to execute SQL queries against SQL warehouses, trigger and monitor job runs, manage clusters, and retrieve run outputs. Requires a Personal Access Token and workspace host URL.',
docsLink: 'https://docs.sim.ai/tools/databricks',
category: 'tools',
bgColor: '#FF3621',
bgColor: '#F9F7F4',
icon: DatabricksIcon,
subBlocks: [
{

View File

@@ -154,7 +154,9 @@ Example:
{
"clxf1nxlb000t0ml79ajwcsj0": true,
"clxf2q43u00010mlh12q9ggx1": false
}`,
}
Return ONLY the JSON object - no explanations, no extra text.`,
placeholder: 'Describe the mailing list subscriptions...',
},
},
@@ -183,7 +185,9 @@ Example:
"signupDate": "2024-01-15T00:00:00Z",
"isActive": true,
"seats": 5
}`,
}
Return ONLY the JSON object - no explanations, no extra text.`,
placeholder: 'Describe the custom properties...',
},
},
@@ -221,7 +225,9 @@ Example:
"name": "John Smith",
"confirmationUrl": "https://example.com/confirm?token=abc123",
"expiresIn": 24
}`,
}
Return ONLY the JSON object - no explanations, no extra text.`,
placeholder: 'Describe the template variables...',
},
},
@@ -261,7 +267,9 @@ Example:
"contentType": "application/pdf",
"data": "JVBERi0xLjQK..."
}
]`,
]
Return ONLY the JSON array - no explanations, no extra text.`,
placeholder: 'Describe the attachments...',
},
},
@@ -300,7 +308,9 @@ Example:
"amount": 49.99,
"currency": "USD",
"isUpgrade": true
}`,
}
Return ONLY the JSON object - no explanations, no extra text.`,
placeholder: 'Describe the event properties...',
},
},
@@ -349,6 +359,7 @@ Example:
{ label: 'Boolean', id: 'boolean' },
{ label: 'Date', id: 'date' },
],
value: () => 'string',
condition: {
field: 'operation',
value: 'create_contact_property',
@@ -363,6 +374,7 @@ Example:
{ label: 'All Properties', id: 'all' },
{ label: 'Custom Only', id: 'custom' },
],
value: () => 'all',
condition: {
field: 'operation',
value: 'list_contact_properties',
@@ -497,23 +509,28 @@ Example:
outputs: {
success: { type: 'boolean', description: 'Whether the operation succeeded' },
id: { type: 'string', description: 'Contact ID (create/update operations)' },
contacts: { type: 'json', description: 'Array of matching contacts (find operation)' },
contacts: {
type: 'json',
description:
'Array of matching contacts (id, email, firstName, lastName, source, subscribed, userGroup, userId, mailingLists, optInStatus)',
},
message: { type: 'string', description: 'Status message (delete operation)' },
mailingLists: {
type: 'json',
description: 'Array of mailing lists (list mailing lists operation)',
description: 'Array of mailing lists (id, name, description, isPublic)',
},
transactionalEmails: {
type: 'json',
description: 'Array of transactional email templates (list transactional emails operation)',
description: 'Array of transactional email templates (id, name, lastUpdated, dataVariables)',
},
pagination: {
type: 'json',
description: 'Pagination info (list transactional emails operation)',
description:
'Pagination info (totalResults, returnedResults, perPage, totalPages, nextCursor, nextPage)',
},
properties: {
type: 'json',
description: 'Array of contact properties (list contact properties operation)',
description: 'Array of contact properties (key, label, type)',
},
},
}

View File

@@ -3998,7 +3998,7 @@ export function LoopsIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg {...props} viewBox='0 0 256 256' fill='none' xmlns='http://www.w3.org/2000/svg'>
<path
fill='currentColor'
fill='#FD4E00'
d='M192.352 88.042c0-7.012-5.685-12.697-12.697-12.697s-12.697 5.685-12.697 12.697c0 .634.052 1.255.142 1.866a25.248 25.248 0 0 0-4.9-.49c-14.006 0-25.36 11.354-25.36 25.36 0 1.63.16 3.222.456 4.765a37.8 37.8 0 0 0-9.296-1.173c-20.95 0-37.935 16.985-37.935 37.935S107.05 194.24 128 194.24s37.935-16.985 37.935-37.935a37.7 37.7 0 0 0-3.78-16.555 25.2 25.2 0 0 0 12.487-3.336 25.2 25.2 0 0 0 4.558 3.336v.02c14.006 0 25.36-11.354 25.36-25.36 0-12.48-9.018-22.855-20.888-24.996a12.6 12.6 0 0 0 8.68-11.972m-77.05 68.263c0-7.012 5.685-12.697 12.697-12.697s12.697 5.685 12.697 12.697c0 7.013-5.685 12.697-12.697 12.697s-12.697-5.685-12.697-12.697'
/>
</svg>
@@ -4542,7 +4542,7 @@ export function DatabricksIcon(props: SVGProps<SVGSVGElement>) {
<svg {...props} viewBox='0 0 241 266' fill='none' xmlns='http://www.w3.org/2000/svg'>
<path
d='M228.085 109.654L120.615 171.674L5.53493 105.41L0 108.475V156.582L120.615 225.911L228.085 164.128V189.596L120.615 251.615L5.53493 185.351L0 188.417V196.67L120.615 266L241 196.67V148.564L235.465 145.498L120.615 211.527L12.9148 149.743V124.275L120.615 186.059L241 116.729V69.3298L235.004 65.7925L120.615 131.585L18.4498 73.1028L120.615 14.3848L204.562 62.7269L211.942 58.4823V52.5869L120.615 0L0 69.3298V76.8759L120.615 146.206L228.085 84.1862V109.654Z'
fill='#F9F7F4'
fill='#FF3621'
/>
</svg>
)

View File

@@ -1,3 +1,4 @@
import { useCallback } from 'react'
import { createLogger } from '@sim/logger'
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import type { WorkflowDeploymentVersionResponse } from '@/lib/workflows/persistence/utils'
@@ -209,6 +210,13 @@ export function useChatDeploymentInfo(workflowId: string | null, options?: { ena
enabled: Boolean(chatId) && statusQuery.isSuccess && (options?.enabled ?? true),
})
const refetch = useCallback(async () => {
const statusResult = await statusQuery.refetch()
if (statusResult.data?.deployment?.id) {
await detailQuery.refetch()
}
}, [statusQuery.refetch, detailQuery.refetch])
return {
isLoading:
statusQuery.isLoading || Boolean(statusQuery.data?.isDeployed && detailQuery.isLoading),
@@ -216,12 +224,7 @@ export function useChatDeploymentInfo(workflowId: string | null, options?: { ena
error: statusQuery.error ?? detailQuery.error,
chatExists: statusQuery.data?.isDeployed ?? false,
existingChat: detailQuery.data ?? null,
refetch: async () => {
await statusQuery.refetch()
if (statusQuery.data?.deployment?.id) {
await detailQuery.refetch()
}
},
refetch,
}
}

View File

@@ -31,7 +31,7 @@ export const greenhouseGetApplicationTool: ToolConfig<
request: {
url: (params: GreenhouseGetApplicationParams) =>
`https://harvest.greenhouse.io/v1/applications/${params.applicationId}`,
`https://harvest.greenhouse.io/v1/applications/${params.applicationId.trim()}`,
method: 'GET',
headers: (params: GreenhouseGetApplicationParams) => ({
Authorization: `Basic ${btoa(`${params.apiKey}:`)}`,

View File

@@ -31,7 +31,7 @@ export const greenhouseGetCandidateTool: ToolConfig<
request: {
url: (params: GreenhouseGetCandidateParams) =>
`https://harvest.greenhouse.io/v1/candidates/${params.candidateId}`,
`https://harvest.greenhouse.io/v1/candidates/${params.candidateId.trim()}`,
method: 'GET',
headers: (params: GreenhouseGetCandidateParams) => ({
Authorization: `Basic ${btoa(`${params.apiKey}:`)}`,

View File

@@ -25,7 +25,7 @@ export const greenhouseGetJobTool: ToolConfig<GreenhouseGetJobParams, Greenhouse
request: {
url: (params: GreenhouseGetJobParams) =>
`https://harvest.greenhouse.io/v1/jobs/${params.jobId}`,
`https://harvest.greenhouse.io/v1/jobs/${params.jobId.trim()}`,
method: 'GET',
headers: (params: GreenhouseGetJobParams) => ({
Authorization: `Basic ${btoa(`${params.apiKey}:`)}`,

View File

@@ -25,7 +25,7 @@ export const greenhouseGetUserTool: ToolConfig<GreenhouseGetUserParams, Greenhou
request: {
url: (params: GreenhouseGetUserParams) =>
`https://harvest.greenhouse.io/v1/users/${params.userId}`,
`https://harvest.greenhouse.io/v1/users/${params.userId.trim()}`,
method: 'GET',
headers: (params: GreenhouseGetUserParams) => ({
Authorization: `Basic ${btoa(`${params.apiKey}:`)}`,

View File

@@ -1,14 +1,14 @@
import { greenhouseGetApplicationTool } from '@/tools/greenhouse/get-application'
import { greenhouseGetCandidateTool } from '@/tools/greenhouse/get-candidate'
import { greenhouseGetJobTool } from '@/tools/greenhouse/get-job'
import { greenhouseGetUserTool } from '@/tools/greenhouse/get-user'
import { greenhouseListApplicationsTool } from '@/tools/greenhouse/list-applications'
import { greenhouseListCandidatesTool } from '@/tools/greenhouse/list-candidates'
import { greenhouseListDepartmentsTool } from '@/tools/greenhouse/list-departments'
import { greenhouseListJobStagesTool } from '@/tools/greenhouse/list-job-stages'
import { greenhouseListJobsTool } from '@/tools/greenhouse/list-jobs'
import { greenhouseListOfficesTool } from '@/tools/greenhouse/list-offices'
import { greenhouseListUsersTool } from '@/tools/greenhouse/list-users'
import { greenhouseGetApplicationTool } from '@/tools/greenhouse/get_application'
import { greenhouseGetCandidateTool } from '@/tools/greenhouse/get_candidate'
import { greenhouseGetJobTool } from '@/tools/greenhouse/get_job'
import { greenhouseGetUserTool } from '@/tools/greenhouse/get_user'
import { greenhouseListApplicationsTool } from '@/tools/greenhouse/list_applications'
import { greenhouseListCandidatesTool } from '@/tools/greenhouse/list_candidates'
import { greenhouseListDepartmentsTool } from '@/tools/greenhouse/list_departments'
import { greenhouseListJobStagesTool } from '@/tools/greenhouse/list_job_stages'
import { greenhouseListJobsTool } from '@/tools/greenhouse/list_jobs'
import { greenhouseListOfficesTool } from '@/tools/greenhouse/list_offices'
import { greenhouseListUsersTool } from '@/tools/greenhouse/list_users'
export {
greenhouseGetApplicationTool,

View File

@@ -87,7 +87,7 @@ export const greenhouseListCandidatesTool: ToolConfig<
if (params.updated_after) url.searchParams.append('updated_after', params.updated_after)
if (params.updated_before) url.searchParams.append('updated_before', params.updated_before)
if (params.job_id) url.searchParams.append('job_id', params.job_id)
if (params.email) url.searchParams.append('email', params.email)
if (params.email) url.searchParams.append('email_address', params.email)
if (params.candidate_ids) url.searchParams.append('candidate_ids', params.candidate_ids)
return url.toString()
},

View File

@@ -45,7 +45,7 @@ export const greenhouseListJobStagesTool: ToolConfig<
request: {
url: (params: GreenhouseListJobStagesParams) => {
const url = new URL(`https://harvest.greenhouse.io/v1/jobs/${params.jobId}/stages`)
const url = new URL(`https://harvest.greenhouse.io/v1/jobs/${params.jobId.trim()}/stages`)
if (params.per_page) url.searchParams.append('per_page', String(params.per_page))
if (params.page) url.searchParams.append('page', String(params.page))
return url.toString()

View File

@@ -95,13 +95,13 @@ export const loopsCreateContactTool: ToolConfig<
Object.assign(body, props)
}
body.email = params.email
if (params.firstName) body.firstName = params.firstName
if (params.lastName) body.lastName = params.lastName
if (params.source) body.source = params.source
body.email = params.email.trim()
if (params.firstName) body.firstName = params.firstName.trim()
if (params.lastName) body.lastName = params.lastName.trim()
if (params.source) body.source = params.source.trim()
if (params.subscribed != null) body.subscribed = params.subscribed
if (params.userGroup) body.userGroup = params.userGroup
if (params.userId) body.userId = params.userId
if (params.userGroup) body.userGroup = params.userGroup.trim()
if (params.userId) body.userId = params.userId.trim()
if (params.mailingLists) {
body.mailingLists =

View File

@@ -46,8 +46,8 @@ export const loopsDeleteContactTool: ToolConfig<
throw new Error('At least one of email or userId is required to delete a contact')
}
const body: Record<string, unknown> = {}
if (params.email) body.email = params.email
if (params.userId) body.userId = params.userId
if (params.email) body.email = params.email.trim()
if (params.userId) body.userId = params.userId.trim()
return body
},
},

View File

@@ -37,8 +37,8 @@ export const loopsFindContactTool: ToolConfig<LoopsFindContactParams, LoopsFindC
throw new Error('At least one of email or userId is required to find a contact')
}
const base = 'https://app.loops.so/api/v1/contacts/find'
if (params.email) return `${base}?email=${encodeURIComponent(params.email)}`
return `${base}?userId=${encodeURIComponent(params.userId!)}`
if (params.email) return `${base}?email=${encodeURIComponent(params.email.trim())}`
return `${base}?userId=${encodeURIComponent(params.userId!.trim())}`
},
method: 'GET',
headers: (params) => ({

View File

@@ -64,8 +64,8 @@ export const loopsSendEventTool: ToolConfig<LoopsSendEventParams, LoopsSendEvent
eventName: params.eventName,
}
if (params.email) body.email = params.email
if (params.userId) body.userId = params.userId
if (params.email) body.email = params.email.trim()
if (params.userId) body.userId = params.userId.trim()
if (params.eventProperties) {
body.eventProperties =

View File

@@ -64,8 +64,8 @@ export const loopsSendTransactionalEmailTool: ToolConfig<
}),
body: (params) => {
const body: Record<string, unknown> = {
email: params.email,
transactionalId: params.transactionalId,
email: params.email.trim(),
transactionalId: params.transactionalId.trim(),
}
if (params.dataVariables) {

View File

@@ -99,13 +99,13 @@ export const loopsUpdateContactTool: ToolConfig<
Object.assign(body, props)
}
if (params.email) body.email = params.email
if (params.userId) body.userId = params.userId
if (params.firstName) body.firstName = params.firstName
if (params.lastName) body.lastName = params.lastName
if (params.source) body.source = params.source
if (params.email) body.email = params.email.trim()
if (params.userId) body.userId = params.userId.trim()
if (params.firstName) body.firstName = params.firstName.trim()
if (params.lastName) body.lastName = params.lastName.trim()
if (params.source) body.source = params.source.trim()
if (params.subscribed != null) body.subscribed = params.subscribed
if (params.userGroup) body.userGroup = params.userGroup
if (params.userGroup) body.userGroup = params.userGroup.trim()
if (params.mailingLists) {
body.mailingLists =