Skip to content

Commit cff57bf

Browse files
committed
fix(monaco): allow clearing tags on update and reject empty audience filter groups at any depth
1 parent 7fc433d commit cff57bf

11 files changed

Lines changed: 76 additions & 20 deletions

File tree

‎apps/docs/content/docs/integrations/monaco.mdx‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ Update fields on an existing Monaco contact by ID. Only the fields sent change.
217217
| `location` | string | No | Location of the contact \(max 256 characters\) |
218218
| `notes` | string | No | Notes about the contact \(max 1,125,000 characters\) |
219219
| `doNotContact` | boolean | No | Whether the contact has opted out of outreach |
220-
| `tags` | array | No | Tag IDs for the contact, as an array or comma-separated list. Replaces the contact's existing tags. |
220+
| `tags` | array | No | Tag IDs for the contact, as an array or comma-separated list. Replaces the contact's existing tags; send \[\] to remove them all. |
221221
| `country` | string | No | Country of the contact, as a country name or code \(max 128 characters\) |
222222
| `jobStartDate` | string | No | Start date of the contact's current job \(YYYY-MM-DD\) |
223223
| `clearFields` | array | No | Fields to clear on the contact, as an array or comma-separated list. Allowed: first_name, last_name, email, title, phone_number, linkedin_url, notes. |
@@ -596,7 +596,7 @@ Update fields on an existing Monaco account by ID. Only the fields sent change.
596596
| `businessModel` | string | No | Business model. One of: B2B, B2C. |
597597
| `companyStatus` | string | No | Operating status of the company. One of: active, acquired, out_of_business, ipo, unknown. |
598598
| `location` | string | No | Headquarters as "city, state, country" |
599-
| `tags` | array | No | Tag IDs for the account, as an array or comma-separated list. Replaces the account's existing tags. |
599+
| `tags` | array | No | Tag IDs for the account, as an array or comma-separated list. Replaces the account's existing tags; send \[\] to remove them all. |
600600
| `domains` | array | No | The account's complete list of domains, primary first, as an array or comma-separated list \(max 500\). Replaces the whole list: domains left out are removed. Send \[\] to remove them all. |
601601
| `resetFields` | array | No | Company fields to reset to the linked company's value, as an array or comma-separated list; later company updates fill them again. Allowed: linkedin_url, description, employee_count, founded_year, estimated_annual_revenue_usd, total_funding_usd, last_funding_round, last_funding_date, industries, business_model, company_status, location, domains. |
602602
| `clearFields` | array | No | Fields to clear on the account, as an array or comma-separated list. Clearing parent_account makes the account top-level and clearing company_id unlinks its company. Allowed: name, notes, owner, parent_account, company_id, linkedin_url, description, employee_count, founded_year, estimated_annual_revenue_usd, total_funding_usd, last_funding_round, last_funding_date, industries, business_model, company_status, location. |
@@ -859,7 +859,7 @@ Update a Monaco opportunity: rename it, change its owner, move it to another sta
859859
| `estimatedCloseDate` | string | No | Expected close date \(YYYY-MM-DD\) |
860860
| `actualCloseDate` | string | No | Actual date the opportunity was closed \(YYYY-MM-DD\) |
861861
| `notes` | string | No | Notes about the opportunity |
862-
| `tags` | array | No | Tag IDs for the opportunity, as an array or comma-separated list. Replaces the opportunity's existing tags. |
862+
| `tags` | array | No | Tag IDs for the opportunity, as an array or comma-separated list. Replaces the opportunity's existing tags; send \[\] to remove them all. |
863863
| `customFields` | json | No | Custom field values to set, as a JSON object keyed by `custom_field_<id>` \(e.g. \{"custom_field_<id>": "value"\}\). Custom fields are always updatable; use Get Field Schemas to find the keys. |
864864

865865
#### Output

‎apps/sim/tools/generated/tool-metadata.ts‎

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

‎apps/sim/tools/monaco/create_audience.ts‎

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { toRecord } from '@sim/utils/object'
1+
import { isRecordLike, toRecord } from '@sim/utils/object'
22
import { ErrorExtractorId } from '@/tools/error-extractors'
33
import { MONACO_ACCESS_TOKEN_PARAM } from '@/tools/monaco/params'
44
import {
@@ -15,6 +15,13 @@ import {
1515
} from '@/tools/monaco/utils'
1616
import type { ToolConfig } from '@/tools/types'
1717

18+
/** Whether a filter list or expression, at any depth, has an empty group, which matches every contact. */
19+
function hasEmptyFilterGroup(value: unknown): boolean {
20+
if (Array.isArray(value)) return value.length === 0 || value.some(hasEmptyFilterGroup)
21+
if (isRecordLike(value) && Array.isArray(value.filters)) return hasEmptyFilterGroup(value.filters)
22+
return false
23+
}
24+
1825
export const monacoCreateAudienceTool: ToolConfig<
1926
MonacoCreateAudienceParams,
2027
MonacoAudienceResponse
@@ -79,9 +86,9 @@ export const monacoCreateAudienceTool: ToolConfig<
7986
const excludedIds = parseMonacoOptionalIdList(params.excludedIds, 'excludedIds')
8087
const sourceAudienceId = params.sourceAudienceId?.trim()
8188

82-
if (Array.isArray(filters) && filters.length === 0) {
89+
if (filters !== undefined && hasEmptyFilterGroup(filters)) {
8390
throw new Error(
84-
'filters must contain at least one rule; an empty list matches every contact'
91+
'filters must not contain an empty list or group; an empty group matches every contact'
8592
)
8693
}
8794
if (contactIds && filters) {

‎apps/sim/tools/monaco/delete_account.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export const monacoDeleteAccountTool: ToolConfig<MonacoDeleteAccountParams, Mona
3434

3535
transformResponse: async (_response, params) => ({
3636
success: true,
37-
output: { id: params?.accountId.trim() ?? '', deleted: true },
37+
output: { id: String(params?.accountId ?? '').trim(), deleted: true },
3838
}),
3939

4040
outputs: {

‎apps/sim/tools/monaco/delete_contact.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export const monacoDeleteContactTool: ToolConfig<MonacoDeleteContactParams, Mona
3333

3434
transformResponse: async (_response, params) => ({
3535
success: true,
36-
output: { id: params?.contactId.trim() ?? '', deleted: true },
36+
output: { id: String(params?.contactId ?? '').trim(), deleted: true },
3737
}),
3838

3939
outputs: {

‎apps/sim/tools/monaco/delete_opportunity.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export const monacoDeleteOpportunityTool: ToolConfig<
3535

3636
transformResponse: async (_response, params) => ({
3737
success: true,
38-
output: { id: params?.opportunityId.trim() ?? '', deleted: true },
38+
output: { id: String(params?.opportunityId ?? '').trim(), deleted: true },
3939
}),
4040

4141
outputs: {

‎apps/sim/tools/monaco/update_account.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export const monacoUpdateAccountTool: ToolConfig<
4242
required: false,
4343
visibility: 'user-or-llm',
4444
description:
45-
"Tag IDs for the account, as an array or comma-separated list. Replaces the account's existing tags.",
45+
"Tag IDs for the account, as an array or comma-separated list. Replaces the account's existing tags; send [] to remove them all.",
4646
},
4747
domains: {
4848
type: 'array',

‎apps/sim/tools/monaco/update_contact.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ export const monacoUpdateContactTool: ToolConfig<
5959
required: false,
6060
visibility: 'user-or-llm',
6161
description:
62-
"Tag IDs for the contact, as an array or comma-separated list. Replaces the contact's existing tags.",
62+
"Tag IDs for the contact, as an array or comma-separated list. Replaces the contact's existing tags; send [] to remove them all.",
6363
},
6464
country: {
6565
type: 'string',

‎apps/sim/tools/monaco/update_opportunity.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ export const monacoUpdateOpportunityTool: ToolConfig<
106106
required: false,
107107
visibility: 'user-or-llm',
108108
description:
109-
"Tag IDs for the opportunity, as an array or comma-separated list. Replaces the opportunity's existing tags.",
109+
"Tag IDs for the opportunity, as an array or comma-separated list. Replaces the opportunity's existing tags; send [] to remove them all.",
110110
items: { type: 'string', description: 'Tag ID' },
111111
},
112112
customFields: {

‎apps/sim/tools/monaco/utils.ts‎

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -450,7 +450,7 @@ export function buildMonacoUpdateContactBody(params: MonacoUpdateContactParams):
450450
setMonacoString(body, 'location', params.location)
451451
setMonacoString(body, 'notes', params.notes)
452452
setMonacoValue(body, 'do_not_contact', params.doNotContact)
453-
setMonacoValue(body, 'tags', parseOptionalMonacoList(params.tags, 'tags'))
453+
setMonacoValue(body, 'tags', parseMonacoListAllowingEmpty(params.tags, 'tags'))
454454
setMonacoString(body, 'country', params.country)
455455
setMonacoString(body, 'job_start_date', params.jobStartDate)
456456
applyMonacoPatchClears(
@@ -468,7 +468,7 @@ function buildMonacoAccountWriteBody(params: MonacoAccountWriteParams): MonacoRe
468468
setMonacoString(body, 'status', params.status)
469469
setMonacoString(body, 'notes', params.notes)
470470
setMonacoString(body, 'owner', params.owner)
471-
setMonacoValue(body, 'tags', parseOptionalMonacoList(params.tags, 'tags'))
471+
setMonacoValue(body, 'tags', parseMonacoListAllowingEmpty(params.tags, 'tags'))
472472
setMonacoString(body, 'parent_account', params.parentAccount)
473473
setMonacoString(body, 'company_id', params.companyId)
474474
setMonacoValue(body, 'replace_edited_fields', params.replaceEditedFields)
@@ -760,11 +760,8 @@ export function buildMonacoOpportunityWriteBody(params: {
760760
body.estimated_value = { currency_code: currencyCode, amount_precise: params.amount }
761761
}
762762

763-
const hasTags =
764-
params.tags !== undefined &&
765-
params.tags !== null &&
766-
!(typeof params.tags === 'string' && params.tags.trim() === '')
767-
if (hasTags) body.tags = parseMonacoIdList(params.tags, 'tags')
763+
const tags = parseMonacoListAllowingEmpty(params.tags, 'tags')
764+
if (tags) body.tags = tags
768765

769766
return body
770767
}

0 commit comments

Comments
 (0)