From 491f9ed73ad50af751e081bdbbecc50c770ddfdf 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 5105d23b217..0eeb4635d3f 100644 --- a/apps/sim/app/api/knowledge/[id]/connectors/route.ts +++ b/apps/sim/app/api/knowledge/[id]/connectors/route.ts @@ -151,19 +151,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}"`) @@ -197,7 +215,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 27f9c6f0d736c81a30fbbfaae02b7bb98e9de952 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 0eeb4635d3f..647c9afcacc 100644 --- a/apps/sim/app/api/knowledge/[id]/connectors/route.ts +++ b/apps/sim/app/api/knowledge/[id]/connectors/route.ts @@ -151,7 +151,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) ?? []) @@ -168,7 +168,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) @@ -181,7 +180,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}"`) @@ -215,7 +214,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 2a8fcb9fb6960523d913fa112dc75df59937583c 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 11b93925e5d125a0a5f8426e2c61a02eac98fc27 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 647c9afcacc..9ce8607a00f 100644 --- a/apps/sim/app/api/knowledge/[id]/connectors/route.ts +++ b/apps/sim/app/api/knowledge/[id]/connectors/route.ts @@ -161,18 +161,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 3f13cb31910d8a471f0c050a18277348f25e6eea 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'