Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions apps/sim/lib/internal/cbinsights/operations/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ import {
asStringArray,
cbInsightsRequest,
compactBody,
parseOptionalStringParam,
} from '@/tools/cbinsights/utils'

export const executeCbinsightsChatOperation: InternalToolOperationImplementation<
CbInsightsChatParams
> = async (params, signal) => {
const message = params.message?.trim()
const message = parseOptionalStringParam(params.message, 'message')
if (!message) throw new Error('CB Insights "message" is required')

return cbInsightsRequest<{
Expand All @@ -25,7 +26,7 @@ export const executeCbinsightsChatOperation: InternalToolOperationImplementation
params,
{
path: '/v2/chatcbi',
body: compactBody({ message, chatID: params.chatId?.trim() }),
body: compactBody({ message, chatID: parseOptionalStringParam(params.chatId, 'chatId') }),
},
(data) => ({
chatId: asString(data.chatID),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
asString,
cbInsightsRequest,
compactBody,
parseOptionalStringParam,
requireOrgId,
} from '@/tools/cbinsights/utils'

Expand All @@ -17,8 +18,8 @@ export const executeCbinsightsGetExitProbabilityHistoryOperation: InternalToolOp
{
path: `/v2/organizations/${orgId}/exitprobabilityhistory`,
body: compactBody({
startDate: params.startDate?.trim(),
endDate: params.endDate?.trim(),
startDate: parseOptionalStringParam(params.startDate, 'startDate'),
endDate: parseOptionalStringParam(params.endDate, 'endDate'),
}),
},
(data) => ({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import type { InternalToolOperationImplementation } from '@/lib/internal/tool-operations/types'
import type { CbInsightsMosaicHistoryParams } from '@/tools/cbinsights/get_mosaic_history'
import { asArray, cbInsightsRequest, compactBody, requireOrgId } from '@/tools/cbinsights/utils'
import {
asArray,
cbInsightsRequest,
compactBody,
parseOptionalStringParam,
requireOrgId,
} from '@/tools/cbinsights/utils'

export const executeCbinsightsGetMosaicHistoryOperation: InternalToolOperationImplementation<
CbInsightsMosaicHistoryParams
Expand All @@ -16,7 +22,7 @@ export const executeCbinsightsGetMosaicHistoryOperation: InternalToolOperationIm
params,
{
path: `/v2/organizations/${orgId}/mosaichistory`,
body: compactBody({ startDate: params.startDate?.trim() }),
body: compactBody({ startDate: parseOptionalStringParam(params.startDate, 'startDate') }),
},
(data) => ({
overall: asArray(data.overall),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
clampLimit,
compactBody,
pageInfo,
parseOptionalStringParam,
requireOrgId,
} from '@/tools/cbinsights/utils'

Expand All @@ -25,7 +26,7 @@ export const executeCbinsightsGetOrgFundingsOperation: InternalToolOperationImpl
path: `/v2/organizations/${orgId}/financialtransactions/fundings`,
body: compactBody({
limit: clampLimit(params.limit),
nextPageToken: params.nextPageToken?.trim(),
nextPageToken: parseOptionalStringParam(params.nextPageToken, 'nextPageToken'),
}),
},
(data) => ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
clampLimit,
compactBody,
pageInfo,
parseOptionalStringParam,
requireOrgId,
} from '@/tools/cbinsights/utils'

Expand All @@ -24,7 +25,7 @@ export const executeCbinsightsGetOrgInvestmentsOperation: InternalToolOperationI
path: `/v2/organizations/${orgId}/financialtransactions/investments`,
body: compactBody({
limit: clampLimit(params.limit),
nextPageToken: params.nextPageToken?.trim(),
nextPageToken: parseOptionalStringParam(params.nextPageToken, 'nextPageToken'),
}),
},
(data) => ({ investments: asArray(data.investments), ...pageInfo(data) }),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
asString,
cbInsightsRequest,
compactBody,
parseOptionalStringParam,
requireOrgIds,
} from '@/tools/cbinsights/utils'

Expand All @@ -17,7 +18,7 @@ export const executeCbinsightsListBusinessRelationshipsOperation: InternalToolOp
path: '/v2/businessrelationships',
body: compactBody({
orgIds: requireOrgIds(params.orgIds),
nextPageToken: params.nextPageToken?.trim(),
nextPageToken: parseOptionalStringParam(params.nextPageToken, 'nextPageToken'),
}),
},
(data) => ({ orgs: asArray(data.orgs), nextPageToken: asString(data.nextPageToken) }),
Expand Down
3 changes: 2 additions & 1 deletion apps/sim/lib/internal/cbinsights/operations/list-fundings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
clampLimit,
compactBody,
pageInfo,
parseOptionalStringParam,
requireOrgIds,
} from '@/tools/cbinsights/utils'

Expand All @@ -24,7 +25,7 @@ export const executeCbinsightsListFundingsOperation: InternalToolOperationImplem
body: compactBody({
orgIds: requireOrgIds(params.orgIds),
limit: clampLimit(params.limit),
nextPageToken: params.nextPageToken?.trim(),
nextPageToken: parseOptionalStringParam(params.nextPageToken, 'nextPageToken'),
}),
},
(data) => ({ orgs: asArray(data.orgs), ...pageInfo(data) }),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
clampLimit,
compactBody,
pageInfo,
parseOptionalStringParam,
requireOrgIds,
} from '@/tools/cbinsights/utils'

Expand All @@ -24,7 +25,7 @@ export const executeCbinsightsListInvestmentsOperation: InternalToolOperationImp
body: compactBody({
orgIds: requireOrgIds(params.orgIds),
limit: clampLimit(params.limit),
nextPageToken: params.nextPageToken?.trim(),
nextPageToken: parseOptionalStringParam(params.nextPageToken, 'nextPageToken'),
}),
},
(data) => ({ orgs: asArray(data.orgs), ...pageInfo(data) }),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
clampLimit,
compactBody,
pageInfo,
parseOptionalStringParam,
requireOrgIds,
} from '@/tools/cbinsights/utils'

Expand All @@ -24,7 +25,7 @@ export const executeCbinsightsListPortfolioExitsOperation: InternalToolOperation
body: compactBody({
orgIds: requireOrgIds(params.orgIds),
limit: clampLimit(params.limit),
nextPageToken: params.nextPageToken?.trim(),
nextPageToken: parseOptionalStringParam(params.nextPageToken, 'nextPageToken'),
}),
},
(data) => ({ orgs: asArray(data.orgs), ...pageInfo(data) }),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
clampLimit,
compactBody,
pageInfo,
parseOptionalStringParam,
parseStringListParam,
} from '@/tools/cbinsights/utils'

Expand All @@ -14,7 +15,7 @@ export const executeCbinsightsLookupOrganizationsOperation: InternalToolOperatio
> = async (params, signal) => {
const names = parseStringListParam(params.names, 'names')
const urls = parseStringListParam(params.urls, 'urls')
const profileUrl = params.profileUrl?.trim()
const profileUrl = parseOptionalStringParam(params.profileUrl, 'profileUrl')

if (!names && !urls && !profileUrl) {
throw new Error('CB Insights lookup requires at least one of "names", "urls", or "profileUrl"')
Expand All @@ -39,7 +40,7 @@ export const executeCbinsightsLookupOrganizationsOperation: InternalToolOperatio
urls,
profileUrl,
limit: clampLimit(params.limit),
nextPageToken: params.nextPageToken?.trim(),
nextPageToken: parseOptionalStringParam(params.nextPageToken, 'nextPageToken'),
}),
},
(data) => ({ orgs: asArray(data.orgs), ...pageInfo(data) }),
Expand Down
11 changes: 8 additions & 3 deletions apps/sim/lib/internal/cbinsights/operations/rag.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
import type { InternalToolOperationImplementation } from '@/lib/internal/tool-operations/types'
import type { CbInsightsRagParams } from '@/tools/cbinsights/rag'
import { asString, asStringArray, cbInsightsRequest } from '@/tools/cbinsights/utils'
import {
asString,
asStringArray,
cbInsightsRequest,
parseOptionalStringParam,
} from '@/tools/cbinsights/utils'

export const executeCbinsightsRagOperation: InternalToolOperationImplementation<
CbInsightsRagParams
> = async (params, signal) => {
const message = params.message?.trim()
const message = parseOptionalStringParam(params.message, 'message')
if (!message) throw new Error('CB Insights "message" is required')
if (message.length > 10_000) {
if (message.length >= 10_000) {
throw new Error('CB Insights "message" must be under 10,000 characters')
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@ import {
parseIntegerParam,
parseNumberParam,
parseOptionalOrgIds,
parseOptionalStringParam,
parseStringListParam,
} from '@/tools/cbinsights/utils'

export const executeCbinsightsSearchFirmographicsOperation: InternalToolOperationImplementation<
CbInsightsFirmographicsParams
> = async (params, signal) => {
const filters = compactBody({
keyword: params.keyword?.trim(),
keyword: parseOptionalStringParam(params.keyword, 'keyword'),
orgIds: parseOptionalOrgIds(params.orgIds),
orgNames: parseStringListParam(params.orgNames, 'orgNames'),
urls: parseStringListParam(params.urls, 'urls'),
Expand Down Expand Up @@ -67,8 +68,8 @@ export const executeCbinsightsSearchFirmographicsOperation: InternalToolOperatio
params.maxValuationInMillions,
'maxValuationInMillions'
),
minLastFundingDate: params.minLastFundingDate?.trim(),
maxLastFundingDate: params.maxLastFundingDate?.trim(),
minLastFundingDate: parseOptionalStringParam(params.minLastFundingDate, 'minLastFundingDate'),
maxLastFundingDate: parseOptionalStringParam(params.maxLastFundingDate, 'maxLastFundingDate'),
vcBacked: parseBooleanParam(params.vcBacked, 'vcBacked'),
})

Expand All @@ -86,13 +87,13 @@ export const executeCbinsightsSearchFirmographicsOperation: InternalToolOperatio
...filters,
...compactBody({
limit: clampLimit(params.limit),
nextPageToken: params.nextPageToken?.trim(),
nextPageToken: parseOptionalStringParam(params.nextPageToken, 'nextPageToken'),
}),
}

/* The API takes one sort object; the block exposes it as two plain fields
so neither has to be typed as JSON. */
const sortField = params.sortField?.trim()
const sortField = parseOptionalStringParam(params.sortField, 'sortField')
if (sortField) {
body.sort = { field: sortField, direction: sortDirection(params.sortDirection) }
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { getErrorMessage } from '@sim/utils/errors'
import type { InternalToolOperationImplementation } from '@/lib/internal/tool-operations/types'
import { sendToolConfirmations } from '@/lib/managed-agents/session-client'
import { normalizeStringList } from '@/tools/managed_agent/normalizers'
import { normalizeScalarText, normalizeStringList } from '@/tools/managed_agent/normalizers'
import { resolveSessionTarget } from '@/tools/managed_agent/shared'
import type {
ManagedAgentToolConfirmationParams,
Expand All @@ -17,7 +17,7 @@ export const executeManagedAgentRespondToolConfirmationOperation: InternalToolOp
return { success: false, output: emptyOutput, error: target.error }
}

const decision = (params.decision ?? '').toString().trim().toLowerCase()
const decision = normalizeScalarText(params.decision).toLowerCase()
if (decision !== 'allow' && decision !== 'deny') {
return {
success: false,
Expand All @@ -36,7 +36,7 @@ export const executeManagedAgentRespondToolConfirmationOperation: InternalToolOp
}
}

const denyMessage = params.denyMessage?.trim()
const denyMessage = normalizeScalarText(params.denyMessage)
try {
await sendToolConfirmations({
apiKey: target.apiKey,
Expand Down
64 changes: 64 additions & 0 deletions apps/sim/tools/cbinsights/cbinsights.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { executeCbinsightsChatOperation } from '@/lib/internal/cbinsights/operations/chat'
import { executeCbinsightsGetCommercialMaturityHistoryOperation } from '@/lib/internal/cbinsights/operations/get-commercial-maturity-history'
import { executeCbinsightsGetExitProbabilityHistoryOperation } from '@/lib/internal/cbinsights/operations/get-exit-probability-history'
import { executeCbinsightsGetOrgFundingsOperation } from '@/lib/internal/cbinsights/operations/get-org-fundings'
import { executeCbinsightsGetOrgOutlookOperation } from '@/lib/internal/cbinsights/operations/get-org-outlook'
import { executeCbinsightsListBusinessRelationshipsOperation } from '@/lib/internal/cbinsights/operations/list-business-relationships'
Expand Down Expand Up @@ -689,3 +690,66 @@ describe('cbinsights model-input projection', () => {
expect(cbinsightsSearchFirmographicsTool.operation.modelInput).toBeUndefined()
})
})

describe('cbinsights non-text runtime values', () => {
/*
* `params.x?.trim()` guards `undefined`, not the type: a block-to-block reference
* resolving to a number reached `.trim()` and threw a bare TypeError naming no
* parameter. The sibling history operation already used parseOptionalStringParam.
*/
it('names the offending date parameter instead of throwing a TypeError', async () => {
mockFetch([AUTH_OK])
await expect(
executeCbinsightsGetExitProbabilityHistoryOperation({
...CREDS,
orgId: 123,
startDate: 20240101,
} as never)
).rejects.toThrow('CB Insights "startDate" must be a string')
})

it('names a non-text keyword on search rather than crashing', async () => {
mockFetch([AUTH_OK])
await expect(
executeCbinsightsSearchFirmographicsOperation({ ...CREDS, keyword: { a: 1 } } as never)
).rejects.toThrow('CB Insights "keyword" must be a string')
})

it('names a non-text page token rather than crashing', async () => {
mockFetch([AUTH_OK])
await expect(
executeCbinsightsListFundingsOperation({
...CREDS,
orgIds: '123',
nextPageToken: 42,
} as never)
).rejects.toThrow('CB Insights "nextPageToken" must be a string')
})

it('still treats a blank optional value as omitted, exactly as before', async () => {
mockFetch([AUTH_OK, { body: {} }])
await executeCbinsightsGetExitProbabilityHistoryOperation({
...CREDS,
orgId: 123,
startDate: ' ',
} as never)
expect(JSON.parse(String(calls[1].init.body))).not.toHaveProperty('startDate')
})
})

describe('cbinsights rag message bound', () => {
/* The tool description and this error both say "under 10,000", so the guard must
reject the boundary value rather than forward it. */
it('rejects a message of exactly 10,000 characters', async () => {
mockFetch([AUTH_OK])
await expect(
executeCbinsightsRagOperation({ ...CREDS, message: 'a'.repeat(10_000) } as never)
).rejects.toThrow('CB Insights "message" must be under 10,000 characters')
})

it('accepts the largest message the contract allows', async () => {
mockFetch([AUTH_OK, { body: { data: 'ok' } }])
await executeCbinsightsRagOperation({ ...CREDS, message: 'a'.repeat(9_999) } as never)
expect(JSON.parse(String(calls[1].init.body)).message).toHaveLength(9_999)
})
})
4 changes: 2 additions & 2 deletions apps/sim/tools/datadog/cancel_downtime.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { CancelDowntimeParams, CancelDowntimeResponse } from '@/tools/datadog/types'
import { datadogErrorMessage, datadogPathSegment } from '@/tools/datadog/utils'
import { datadogErrorMessage, datadogPathSegment, resolveDatadogSite } from '@/tools/datadog/utils'
import type { ToolConfig } from '@/tools/types'

export const cancelDowntimeTool: ToolConfig<CancelDowntimeParams, CancelDowntimeResponse> = {
Expand Down Expand Up @@ -37,7 +37,7 @@ export const cancelDowntimeTool: ToolConfig<CancelDowntimeParams, CancelDowntime

request: {
url: (params) => {
const site = params.site || 'datadoghq.com'
const site = resolveDatadogSite(params.site)
const downtimeId = datadogPathSegment(params.downtimeId)
return `https://api.${site}/api/v2/downtime/${downtimeId}`
},
Expand Down
Loading
Loading