Waleed e0551b3a1e improvement(kb-connectors): multi-select fields + Slack bot/app message extraction (#4711)
* improvement(kb-connectors): multi-select fields + Slack bot/app message extraction

Adds multi-value support to KB connector configuration fields and applies it
across 8 connectors: Jira (projects), Confluence (spaces), Slack (channels),
Microsoft Teams (channels), Google Calendar (calendars), Gmail (labels),
Notion (databases), and Linear (teams + projects). Each connector emits
byte-identical externalId for legacy single-value configs so existing rows
reconcile in place via the sync engine's externalId-keyed matching.

Framework changes:
- ConnectorConfigField gains `multi?: boolean`
- New `parseMultiValue` helper in @/connectors/utils
- useConnectorConfigFields state model upgraded to string|string[]
- ConnectorSelectorField renders Combobox in multi-select mode when `field.multi`
- Add/edit connector modals handle array values end-to-end

Per-connector specifics:
- Jira: JQL `project in (...)` for 2+ keys, `project = X` for one
- Confluence: routes through CQL `space in (...)` when multi; v2 fast path stays
  for single+no-label; also fixes selector returning space.id instead of space.key
- Slack: loops per channel emitting one document each; extracts text from
  attachments and Block Kit blocks (incl. nested attachment.blocks where GitHub
  embeds PR bodies); contentHash bumped to slack-v2: to force one-time re-embed
- Microsoft Teams: loops per channel within a single team
- Google Calendar: compound cursor across calendars; single-calendar keeps
  legacy externalId/contentHash for zero-churn
- Gmail: (label:A OR label:B) with quoted-form for labels with spaces
- Notion: sequential walk via JSON compound cursor; single-DB keeps bare cursor
- Linear: GraphQL IdComparator.in for multi, eq for single

* fix(kb-connectors): valuesEqual treats legacy scalar as equal to multi-array

Existing connectors created before multi-select store sourceConfig values as
scalars (e.g. projectKey: "ENG"). With the field now declared multi: true,
resolveSourceConfig returns an array (["ENG"]), and the original valuesEqual
fell through to a strict reference comparison — falsely flagging unsaved
changes on open and triggering an unnecessary string→array shape rewrite on
save.

valuesEqual now normalizes both sides to string[] via CSV-split when either
is an array, so persisted scalar and in-memory array of the same content
compare equal. Single-value (non-multi) fields keep strict string equality.

* fix(kb-connectors): GCal externalId on config downgrade, Slack silent skip, valuesEqual order

- google-calendar getDocument: derive isMultiCalendar from the externalId's
  `:` separator instead of the current config count. Prevents duplicates when
  a user downgrades from multi to single calendar — previously the returned
  doc lost its `calendarId:` prefix and was treated as a new row by the sync
  engine, orphaning the original.
- slack listDocuments: throw on unresolvable channel instead of silently
  skipping. Matches MS Teams behaviour. Silent skip would let the sync
  engine orphan-delete the previously indexed channel content if a bot was
  removed or a channel was archived/renamed mid-life.
- edit-connector-modal valuesEqual: order-insensitive comparison for multi-
  select arrays via Set membership. Multi-select UI doesn't guarantee
  insertion order matches the server-returned order, so `["A","B"]` vs
  `["B","A"]` would otherwise flag false unsaved changes.

* chore(kb-connectors): use emptyValue() fallback in isFieldPopulated for consistency

Behavior unchanged — isValuePopulated('') and isValuePopulated([]) both return
false — but reading the field-typed fallback inline matches the convention
used elsewhere in the hook (coerceForField, handleFieldChange, resolveSourceConfig).

* fix(kb-connectors): Linear projects selector loads across all selected teams

When the team selector is in multi-select mode, the basic-mode projects
dropdown was passing only the first team ID into the linear.projects
selector context (via readFirst in resolveDepValue), so projects from other
selected teams were invisible.

resolveDepValue now joins multi-value parents into a CSV string so dependent
selectors receive every selected parent ID. The /api/tools/linear/projects
route splits the CSV teamId, fetches projects from each team in parallel,
and dedupes by project ID. Single-team configs pass through unchanged
(`split(",")` on a bare ID yields a one-element array).

The AND-of-filters semantics in buildIssuesQuery is intentional and matches
standard GraphQL filter behavior — a user filtering on teams [A,B] and
projects [X,Y] gets issues in (A or B) AND (X or Y). With this fix the
project dropdown now shows every project under any selected team so the
user can compose the right project set.

* fix(gmail-connector): always wrap OR-containing custom query, not just unwrapped ones

The previous check `!/^\(.*\)$/.test(trimmedCustom)` was supposed to avoid
double-wrapping an already-parenthesized expression, but it false-positives
on inputs like `(from:alice) OR (from:bob)` where the parens don't bracket
the whole string. Those would skip wrapping and the top-level OR would bind
across the preceding label / category / date filters instead of the custom
clause.

Always wrap when an OR is present — double-parens are a no-op in Gmail
search syntax, so `((from:a OR from:b))` parses the same as `(from:a OR
from:b)`. Simpler than walking parens depth and provably safe.
2026-05-21 19:41:45 -07:00

Sim Logo

The open-source platform to build AI agents and run your agentic workforce. Connect 1,000+ integrations and LLMs to orchestrate agentic workflows.

Sim.ai Discord Twitter Documentation

Ask DeepWiki Set Up with Cursor

Build Workflows with Ease

Design agent workflows visually on a canvas—connect agents, tools, and blocks, then run them instantly.

Workflow Builder Demo

Supercharge with Copilot

Leverage Copilot to generate nodes, fix errors, and iterate on flows directly from natural language.

Copilot Demo

Integrate Vector Databases

Upload documents to a vector store and let agents answer questions grounded in your specific content.

Knowledge Uploads and Retrieval Demo

Quickstart

Cloud-hosted: sim.ai

Sim.ai

Self-hosted: NPM Package

npx simstudio

→ http://localhost:3000

Note

Docker must be installed and running on your machine.

Options

Flag Description
-p, --port <port> Port to run Sim on (default 3000)
--no-pull Skip pulling latest Docker images

Self-hosted: Docker Compose

git clone https://github.com/simstudioai/sim.git && cd sim
docker compose -f docker-compose.prod.yml up -d

Open http://localhost:3000

Sim also supports local models via Ollama and vLLM — see the Docker self-hosting docs for setup details.

Self-hosted: Manual Setup

Requirements: Bun, Node.js v20+, PostgreSQL 12+ with pgvector

  1. Clone and install:
git clone https://github.com/simstudioai/sim.git
cd sim
bun install
bun run prepare  # Set up pre-commit hooks
  1. Set up PostgreSQL with pgvector:
docker run --name simstudio-db -e POSTGRES_PASSWORD=your_password -e POSTGRES_DB=simstudio -p 5432:5432 -d pgvector/pgvector:pg17

Or install manually via the pgvector guide.

  1. Configure environment:
cp apps/sim/.env.example apps/sim/.env
# Create your secrets
perl -i -pe "s/your_encryption_key/$(openssl rand -hex 32)/" apps/sim/.env
perl -i -pe "s/your_internal_api_secret/$(openssl rand -hex 32)/" apps/sim/.env
perl -i -pe "s/your_api_encryption_key/$(openssl rand -hex 32)/" apps/sim/.env
# DB configs for migration
cp packages/db/.env.example packages/db/.env
# Edit both .env files to set DATABASE_URL="postgresql://postgres:your_password@localhost:5432/simstudio"
  1. Run migrations:
cd packages/db && bun run db:migrate
  1. Start development servers:
bun run dev:full  # Starts Next.js app and realtime socket server

Or run separately: bun run dev (Next.js) and cd apps/sim && bun run dev:sockets (realtime).

Copilot API Keys

Copilot is a Sim-managed service. To use Copilot on a self-hosted instance:

  • Go to https://sim.ai → Settings → Copilot and generate a Copilot API key
  • Set COPILOT_API_KEY environment variable in your self-hosted apps/sim/.env file to that value

Environment Variables

See the environment variables reference for the full list, or apps/sim/.env.example for defaults.

Tech Stack

Contributing

We welcome contributions! Please see our Contributing Guide for details.

License

This project is licensed under the Apache License 2.0 - see the LICENSE file for details.

Made with ❤️ by the Sim Team

Languages
TypeScript 77%
MDX 20.8%
JavaScript 1.9%
CSS 0.1%