From e12f15f5a7f5e31e6f34b07bc41ee85bbe3381bd Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Fri, 3 Apr 2026 15:06:26 -0700 Subject: [PATCH 1/5] fix(kb): fix Linear connector GraphQL type errors and tag slot reuse --- .../api/knowledge/[id]/connectors/route.ts | 24 ++++++++++++++++--- apps/sim/connectors/linear/linear.ts | 6 ++--- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/apps/sim/app/api/knowledge/[id]/connectors/route.ts b/apps/sim/app/api/knowledge/[id]/connectors/route.ts index 4b01430f166..d7164d54fa3 100644 --- a/apps/sim/app/api/knowledge/[id]/connectors/route.ts +++ b/apps/sim/app/api/knowledge/[id]/connectors/route.ts @@ -162,19 +162,37 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{ } const tagSlotMapping: Record = {} + const newTagSlotMapping: Record = {} if (connectorConfig.tagDefinitions?.length) { const disabledIds = new Set((sourceConfig.disabledTagIds as string[] | undefined) ?? []) const enabledDefs = connectorConfig.tagDefinitions.filter((td) => !disabledIds.has(td.id)) const existingDefs = await db - .select({ tagSlot: knowledgeBaseTagDefinitions.tagSlot }) + .select({ + tagSlot: knowledgeBaseTagDefinitions.tagSlot, + displayName: knowledgeBaseTagDefinitions.displayName, + }) .from(knowledgeBaseTagDefinitions) .where(eq(knowledgeBaseTagDefinitions.knowledgeBaseId, knowledgeBaseId)) const usedSlots = new Set(existingDefs.map((d) => d.tagSlot)) - const { mapping, skipped: skippedTags } = allocateTagSlots(enabledDefs, usedSlots) + const existingByName = new Map(existingDefs.map((d) => [d.displayName, d.tagSlot])) + + /** Reuse existing tag definitions that match by display name */ + const defsNeedingSlots: typeof enabledDefs = [] + for (const td of enabledDefs) { + const existingSlot = existingByName.get(td.displayName) + if (existingSlot) { + tagSlotMapping[td.id] = existingSlot + } else { + defsNeedingSlots.push(td) + } + } + + const { mapping, skipped: skippedTags } = allocateTagSlots(defsNeedingSlots, usedSlots) Object.assign(tagSlotMapping, mapping) + Object.assign(newTagSlotMapping, mapping) for (const name of skippedTags) { logger.warn(`[${requestId}] No available slots for "${name}"`) @@ -208,7 +226,7 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{ throw new Error('Knowledge base not found') } - for (const [semanticId, slot] of Object.entries(tagSlotMapping)) { + for (const [semanticId, slot] of Object.entries(newTagSlotMapping)) { const td = connectorConfig.tagDefinitions!.find((d) => d.id === semanticId)! await createTagDefinition( { diff --git a/apps/sim/connectors/linear/linear.ts b/apps/sim/connectors/linear/linear.ts index cd779b3385f..fbcb2dde60e 100644 --- a/apps/sim/connectors/linear/linear.ts +++ b/apps/sim/connectors/linear/linear.ts @@ -119,7 +119,7 @@ const ISSUE_FIELDS = ` ` const ISSUE_BY_ID_QUERY = ` - query GetIssue($id: String!) { + query GetIssue($id: ID!) { issue(id: $id) { ${ISSUE_FIELDS} } @@ -147,13 +147,13 @@ function buildIssuesQuery(sourceConfig: Record): { const variables: Record = {} if (teamId) { - varDefs.push('$teamId: String!') + varDefs.push('$teamId: ID!') filterClauses.push('team: { id: { eq: $teamId } }') variables.teamId = teamId } if (projectId) { - varDefs.push('$projectId: String!') + varDefs.push('$projectId: ID!') filterClauses.push('project: { id: { eq: $projectId } }') variables.projectId = projectId } From 77839f9d5a2d376d3540e63b9ce200d32b78f183 Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Fri, 3 Apr 2026 15:33:44 -0700 Subject: [PATCH 2/5] fix(kb): simplify tag slot reuse, revert Linear GraphQL types to String Clean up newTagSlotMapping into direct assignment, remove unnecessary comment, and revert ID! back to String! to match Linear SDK types. Co-Authored-By: Claude Opus 4.6 --- apps/sim/app/api/knowledge/[id]/connectors/route.ts | 7 +++---- apps/sim/connectors/linear/linear.ts | 6 +++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/apps/sim/app/api/knowledge/[id]/connectors/route.ts b/apps/sim/app/api/knowledge/[id]/connectors/route.ts index d7164d54fa3..3b34cdf4c03 100644 --- a/apps/sim/app/api/knowledge/[id]/connectors/route.ts +++ b/apps/sim/app/api/knowledge/[id]/connectors/route.ts @@ -162,7 +162,7 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{ } const tagSlotMapping: Record = {} - const newTagSlotMapping: Record = {} + let newTagSlots: Record = {} if (connectorConfig.tagDefinitions?.length) { const disabledIds = new Set((sourceConfig.disabledTagIds as string[] | undefined) ?? []) @@ -179,7 +179,6 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{ const usedSlots = new Set(existingDefs.map((d) => d.tagSlot)) const existingByName = new Map(existingDefs.map((d) => [d.displayName, d.tagSlot])) - /** Reuse existing tag definitions that match by display name */ const defsNeedingSlots: typeof enabledDefs = [] for (const td of enabledDefs) { const existingSlot = existingByName.get(td.displayName) @@ -192,7 +191,7 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{ const { mapping, skipped: skippedTags } = allocateTagSlots(defsNeedingSlots, usedSlots) Object.assign(tagSlotMapping, mapping) - Object.assign(newTagSlotMapping, mapping) + newTagSlots = mapping for (const name of skippedTags) { logger.warn(`[${requestId}] No available slots for "${name}"`) @@ -226,7 +225,7 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{ throw new Error('Knowledge base not found') } - for (const [semanticId, slot] of Object.entries(newTagSlotMapping)) { + for (const [semanticId, slot] of Object.entries(newTagSlots)) { const td = connectorConfig.tagDefinitions!.find((d) => d.id === semanticId)! await createTagDefinition( { diff --git a/apps/sim/connectors/linear/linear.ts b/apps/sim/connectors/linear/linear.ts index fbcb2dde60e..cd779b3385f 100644 --- a/apps/sim/connectors/linear/linear.ts +++ b/apps/sim/connectors/linear/linear.ts @@ -119,7 +119,7 @@ const ISSUE_FIELDS = ` ` const ISSUE_BY_ID_QUERY = ` - query GetIssue($id: ID!) { + query GetIssue($id: String!) { issue(id: $id) { ${ISSUE_FIELDS} } @@ -147,13 +147,13 @@ function buildIssuesQuery(sourceConfig: Record): { const variables: Record = {} if (teamId) { - varDefs.push('$teamId: ID!') + varDefs.push('$teamId: String!') filterClauses.push('team: { id: { eq: $teamId } }') variables.teamId = teamId } if (projectId) { - varDefs.push('$projectId: ID!') + varDefs.push('$projectId: String!') filterClauses.push('project: { id: { eq: $projectId } }') variables.projectId = projectId } From bcae263923928cf32805df578f5ac180edf749eb Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Fri, 3 Apr 2026 15:35:27 -0700 Subject: [PATCH 3/5] fix(kb): use ID! type for Linear GraphQL filter variables --- apps/sim/connectors/linear/linear.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/sim/connectors/linear/linear.ts b/apps/sim/connectors/linear/linear.ts index cd779b3385f..fbcb2dde60e 100644 --- a/apps/sim/connectors/linear/linear.ts +++ b/apps/sim/connectors/linear/linear.ts @@ -119,7 +119,7 @@ const ISSUE_FIELDS = ` ` const ISSUE_BY_ID_QUERY = ` - query GetIssue($id: String!) { + query GetIssue($id: ID!) { issue(id: $id) { ${ISSUE_FIELDS} } @@ -147,13 +147,13 @@ function buildIssuesQuery(sourceConfig: Record): { const variables: Record = {} if (teamId) { - varDefs.push('$teamId: String!') + varDefs.push('$teamId: ID!') filterClauses.push('team: { id: { eq: $teamId } }') variables.teamId = teamId } if (projectId) { - varDefs.push('$projectId: String!') + varDefs.push('$projectId: ID!') filterClauses.push('project: { id: { eq: $projectId } }') variables.projectId = projectId } From 6e0d0164213bf48d1c4dd710f25688b16153fabd Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Fri, 3 Apr 2026 15:37:11 -0700 Subject: [PATCH 4/5] fix(kb): verify field type when reusing existing tag slots Add fieldType check to the tag slot reuse logic so a connector with a matching displayName but different fieldType falls through to fresh slot allocation instead of silently reusing an incompatible slot. Co-Authored-By: Claude Opus 4.6 --- apps/sim/app/api/knowledge/[id]/connectors/route.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/apps/sim/app/api/knowledge/[id]/connectors/route.ts b/apps/sim/app/api/knowledge/[id]/connectors/route.ts index 3b34cdf4c03..92bd8013ee9 100644 --- a/apps/sim/app/api/knowledge/[id]/connectors/route.ts +++ b/apps/sim/app/api/knowledge/[id]/connectors/route.ts @@ -172,18 +172,21 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{ .select({ tagSlot: knowledgeBaseTagDefinitions.tagSlot, displayName: knowledgeBaseTagDefinitions.displayName, + fieldType: knowledgeBaseTagDefinitions.fieldType, }) .from(knowledgeBaseTagDefinitions) .where(eq(knowledgeBaseTagDefinitions.knowledgeBaseId, knowledgeBaseId)) const usedSlots = new Set(existingDefs.map((d) => d.tagSlot)) - const existingByName = new Map(existingDefs.map((d) => [d.displayName, d.tagSlot])) + const existingByName = new Map( + existingDefs.map((d) => [d.displayName, { tagSlot: d.tagSlot, fieldType: d.fieldType }]) + ) const defsNeedingSlots: typeof enabledDefs = [] for (const td of enabledDefs) { - const existingSlot = existingByName.get(td.displayName) - if (existingSlot) { - tagSlotMapping[td.id] = existingSlot + const existing = existingByName.get(td.displayName) + if (existing && existing.fieldType === td.fieldType) { + tagSlotMapping[td.id] = existing.tagSlot } else { defsNeedingSlots.push(td) } From 8b7d5f537d4fe176c803a1f70723fe5c3cd45716 Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Fri, 3 Apr 2026 15:57:44 -0700 Subject: [PATCH 5/5] fix(kb): enable search on connector selector dropdowns --- .../add-connector-modal/components/connector-selector-field.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/add-connector-modal/components/connector-selector-field.tsx b/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/add-connector-modal/components/connector-selector-field.tsx index 441a4a9313f..b2b14cc536c 100644 --- a/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/add-connector-modal/components/connector-selector-field.tsx +++ b/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/add-connector-modal/components/connector-selector-field.tsx @@ -79,6 +79,8 @@ export function ConnectorSelectorField({ options={comboboxOptions} value={value || undefined} onChange={onChange} + searchable + searchPlaceholder={`Search ${field.title.toLowerCase()}...`} placeholder={ !credentialId ? 'Connect an account first'