You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: .agents/skills/add-block/SKILL.md
+17-85Lines changed: 17 additions & 85 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -15,23 +15,9 @@ When the user asks you to create a block:
15
15
2. Configure all subBlocks with proper types, conditions, and dependencies
16
16
3. Wire up tools correctly
17
17
18
-
## Hard Rule: No Guessed Tool Outputs
18
+
## No guessed tool outputs
19
19
20
-
Blocks depend on tool outputs. If the underlying tool response schema is not documented or live-verified, you MUST tell the user instead of guessing block outputs.
21
-
22
-
When block work changes tool execution, same-process work must use a registered
23
-
`InternalToolConfig.operation`. Never add a Sim `/api/...` self-hop or the retired
24
-
`directExecution` property.
25
-
26
-
- Do NOT invent block outputs for undocumented tool responses
27
-
- Do NOT describe unknown JSON shapes as if they were confirmed
28
-
- Do NOT wire fields into the block just because they seem likely to exist
29
-
30
-
If the tool outputs are not known, do one of these instead:
31
-
1. Ask the user for sample tool responses
32
-
2. Ask the user for test credentials so the tool responses can be verified
33
-
3. Limit the block to operations whose outputs are documented
34
-
4. Leave uncertain outputs out and explicitly tell the user what remains unknown
20
+
Block outputs mirror tool outputs. When a tool's response schema is neither documented nor live-verified, don't infer field names or JSON shapes — ask the user for sample responses or test credentials, limit the block to operations whose outputs are documented, or leave the uncertain outputs out and say exactly what remains unknown.
35
21
36
22
## Block Configuration Structure
37
23
@@ -323,12 +309,10 @@ When several fields are mutually exclusive alternatives, mark them all `required
323
309
"exactly one" at execution — a conditionally-required canonical pair rejects the workflow before the
324
310
other paths ever get a chance to supply the value.
325
311
326
-
**Critical constraints:**
327
-
-`canonicalParamId` must NOT match any subblock's `id` in the same block
328
-
- A canonical group is **block-wide**, not per-operation: `buildCanonicalIndex` keys groups by
329
-
`canonicalParamId` across every subblock, and a group has exactly one `basicId`. Two operations
330
-
that each need a file pair need two distinct `canonicalParamId` values.
331
-
- All members of a group must share the same `required` status
312
+
**Constraints (block-wide):**
313
+
-`canonicalParamId` must not equal any subblock `id` in the block.
314
+
- One canonical id links exactly one basic/advanced pair for one logical parameter. Groups are keyed by canonical id across every subblock and hold one `basicId`, so two operations that each need a pair need two canonical ids.
315
+
- All members of a group share the same `required` status.
332
316
333
317
### Normalizing File Input in tools.config
334
318
@@ -548,12 +532,6 @@ Maps multiple UI fields to a single serialized parameter:
548
532
- In advanced mode: `channelId` input value → `params.channel`
549
533
- The serializer consolidates based on current mode
550
534
551
-
**Critical constraints:**
552
-
-`canonicalParamId` must NOT match any other subblock's `id` in the same block (causes conflicts)
553
-
- A `canonicalParamId` links exactly one basic/advanced pair for a single logical parameter. Do NOT reuse the same `canonicalParamId` for different parameters, even under mutually-exclusive conditions/operations
554
-
- ONLY use `canonicalParamId` to link basic/advanced mode alternatives for the same logical parameter
555
-
- Do NOT use it for any other purpose
556
-
557
535
## WandConfig Pattern
558
536
559
537
Enables AI-assisted field generation.
@@ -581,9 +559,9 @@ Enables AI-assisted field generation.
581
559
-`'sql-query'` - SQL statements
582
560
-`'timestamp'` - Adds current date/time context
583
561
584
-
## Tools Configuration
562
+
Use `wandConfig` on fields that are hard to fill by hand — timestamps (`generationType: 'timestamp'` injects the current date), comma-separated ID lists, complex query strings. Keep the prompt specific about the return format (e.g. 'Return ONLY the ISO 8601 timestamp string').
585
563
586
-
**Important:**`tools.config.tool` runs during serialization before variable resolution. Put `Number()` and other type coercions in `tools.config.params` instead, which runs at execution time after variables are resolved.
564
+
## Tools Configuration
587
565
588
566
**Preferred:** Use tool names directly as dropdown option IDs to avoid switch cases:
589
567
```typescript
@@ -654,19 +632,12 @@ outputs: {
654
632
// Use type: 'json' for complex objects or arrays (NOT type: 'array' with items)
655
633
items: { type: 'json', description: 'List of items' },
When using `type: 'json'` and you know the object shape in advance, **describe the inner fields in the description** so downstream blocks know what properties are available. Block outputs have no nested `properties` form — always keep the output flat and put the shape in the `description`:
640
+
When using `type: 'json'` and you know the object shape in advance, **describe the inner fields in the description** so downstream blocks know what properties are available. Keep the output flat and put the shape in the `description`:
670
641
671
642
```typescript
672
643
outputs: {
@@ -681,10 +652,6 @@ outputs: {
681
652
}
682
653
```
683
654
684
-
Nested object outputs (`plan: { id: { type: 'string' }, ... }`) are a **tool-output** feature only — `OutputFieldDefinition` for blocks does not allow them and they fail TypeScript at build time.
685
-
686
-
If the output shape is unknown because the underlying tool response is undocumented, you MUST tell the user and stop. Unknown is not the same as variable. Never guess block outputs.
After creating the block, remind the user to register it in `apps/sim/blocks/registry-maps.ts`(the data maps live here; `registry.ts` holds only the accessor functions). Add the import and an entry to each map alphabetically:
697
+
Register the blockin `apps/sim/blocks/registry-maps.ts`— add the import and an entry to each map alphabetically:
@@ -887,41 +854,6 @@ Optional fields that are rarely used should be set to `mode: 'advanced'` so they
887
854
}
888
855
```
889
856
890
-
## WandConfig for Complex Inputs
891
-
892
-
Use `wandConfig` for fields that are hard to fill out manually, such as timestamps, comma-separated lists, and complex query strings. This gives users an AI-assisted input experience.
893
-
894
-
```typescript
895
-
// Timestamps - use generationType: 'timestamp' to inject current date context
896
-
{
897
-
id: 'startTime',
898
-
title: 'Start Time',
899
-
type: 'short-input',
900
-
mode: 'advanced',
901
-
wandConfig: {
902
-
enabled: true,
903
-
prompt: 'Generate an ISO 8601 timestamp based on the user description. Return ONLY the timestamp string.',
904
-
generationType: 'timestamp',
905
-
},
906
-
}
907
-
908
-
// Comma-separated lists - simple prompt without generationType
909
-
{
910
-
id: 'mediaIds',
911
-
title: 'Media IDs',
912
-
type: 'short-input',
913
-
mode: 'advanced',
914
-
wandConfig: {
915
-
enabled: true,
916
-
prompt: 'Generate a comma-separated list of media IDs. Return ONLY the comma-separated values.',
917
-
},
918
-
}
919
-
```
920
-
921
-
## Naming Convention
922
-
923
-
All tool IDs referenced in `tools.access` and returned by `tools.config.tool` MUST use `snake_case` (e.g., `x_create_tweet`, `slack_send_message`). Never use camelCase or PascalCase.
924
-
925
857
## BlockMeta (Required)
926
858
927
859
Every block file must export a `{Service}BlockMeta` alongside the block — **minimum 7 templates**. Look at existing examples in `apps/sim/blocks/blocks/` (e.g. `browser_use.ts`, `google_sheets.ts`) for the pattern.
@@ -998,7 +930,7 @@ bun run apps/sim/scripts/check-canvas-sentences.ts --block={service}
998
930
Adding a block on its own needs no **tool metadata** regeneration — a block references existing
999
931
tool IDs through `tools.access` and does not change any tool's shape.
1000
932
1001
-
But if the same change also adds, edits **or removes** a tool, run `bun run tool-metadata:generate` and commit the result, or CI fails on stale artifacts. That matters here because a block's `outputs` are authored to match its tools' outputs, and the UI now reads those from the generated metadata rather than the executable registry — an unregenerated tool change makes the block's outputs disagree with what the panel renders. See `.agents/skills/tool-registry-boundary/SKILL.md`.
933
+
But if the same change also adds, edits **or removes** a tool, run `bun run tool-metadata:generate` and commit the result, or CI fails on stale artifacts. That matters here because a block's `outputs` are authored to match its tools' outputs, and the UI reads those from the generated metadata, not the executable registry — an unregenerated tool change makes the block's outputs disagree with what the panel renders. See `.agents/skills/tool-registry-boundary/SKILL.md`.
1002
934
1003
935
A visible integration block does require the generated integration catalog and docs to be refreshed.
1004
936
After adding or changing one, run:
@@ -1052,9 +984,9 @@ changes.
1052
984
1053
985
## Final Validation (Required)
1054
986
1055
-
After creating the block, you MUST validate it against every tool it references:
987
+
Validate the blockagainst every tool in `tools.access`:
1056
988
1057
-
1.**Read every tool definition**that appears in `tools.access` — do not skip any
989
+
1.**Read each tool definition** in `tools.access`
1058
990
2.**For each tool, verify the block has correct:**
1059
991
- SubBlock inputs that cover all required tool params (with correct `condition` to show for that operation)
1060
992
- SubBlock input types that match the tool param types (e.g., dropdown for enums, short-input for strings)
@@ -1063,11 +995,11 @@ After creating the block, you MUST validate it against every tool it references:
1063
995
3.**Verify block outputs** cover the key fields returned by all tools
1064
996
4.**Verify conditions** — each subBlock should only show for the operations that actually use it
1065
997
5.**Verify `{Service}BlockMeta` is exported** with at least 7 templates, each having `icon`, `title`, `prompt`, `modules`, `category`, and `tags`
1066
-
6.**If any tool outputs are still unknown**, explicitly tell the user instead of guessing block outputs
998
+
6.**List any tool outputs still unknown** rather than guessing block outputs
1067
999
7.**Verify the tool execution boundary** — blocks never create or call API routes. Every referenced
1068
1000
tool must already be either a registered `InternalToolConfig.operation` or an absolute external
1069
-
HTTP(S) `ToolConfig.request`. If transport needs to change, use the `add-tools` skill; do not add a
1070
-
same-origin `/api/...` hop from the block.
1001
+
HTTP(S) `ToolConfig.request`. If transport needs to change, use the `add-tools` skill; never add a
1002
+
same-origin `/api/...` hop or a `directExecution` property from the block.
1071
1003
1072
1004
## Option Lists: `selectorKey` or `options`, never a per-block fetcher
1073
1005
@@ -1103,7 +1035,7 @@ options: (params) => {
1103
1035
}
1104
1036
```
1105
1037
1106
-
**Never fetch inside `options`, and never reach into the stores from a block definition.** A fetcher that resolves its credential with `readSubBlockValue(blockId, ...)` only works on the canvas — every surface that is not the editor gets an empty list.`fetchOptions`/`fetchOptionById` were removed for exactly this reason.
1038
+
**Never fetch inside `options`, and never reach into the stores from a block definition.** A fetcher that resolves its credential with `readSubBlockValue(blockId, ...)` only works on the canvas — every surface that is not the editor gets an empty list.
Copy file name to clipboardExpand all lines: .agents/skills/add-column-type/SKILL.md
+3-3Lines changed: 3 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -8,7 +8,7 @@ argument-hint: <type-name>
8
8
9
9
A column type is **one file** in `apps/sim/lib/table/column-types/` plus a registry entry. Everything that varies per type — label, icon, storage cast, coercion, validation, conversion compatibility, formatting, editor, filter operators — lives on that one object, so no consumer needs editing.
10
10
11
-
This was not always true: adding `currency` originally took ~40 edits across 32 `switch` arms and 26 UI branches, each of which failed **silently** when missed. The registry exists to make that impossible, so the rule is absolute: **if you find yourself adding a `case 'yourtype':` anywhere outside `column-types/`, the registry is missing a field. Add the field instead.**
11
+
A `case 'yourtype':` outside `column-types/` fails **silently** when missed (a wrong `jsonbCast` breaks every filter on the column). The registry exists to make that impossible, so the rule is absolute with one documented exception (`import.ts`'s `coerceValue`, see "Traps" below): **if you find yourself adding a `case 'yourtype':` anywhere else outside `column-types/`, the registry is missing a field. Add the field instead.**
12
12
13
13
## Hard Rule: the compiler tells you what to do
14
14
@@ -116,9 +116,9 @@ Prefer set-based SQL. When the transform genuinely needs JS (`currency`'s separa
116
116
117
117
## Watch out
118
118
119
-
-**Import cycles.**`column-types/select.ts` imports `select-values.ts`, so `select-values.ts` must **not** import the registry — that closes a cycle and fails at module init. Inside a type's own helper module the string literal is the implementation, not a config leak.
119
+
-**Import cycles.**`column-types/select.ts` imports `lib/table/select-values.ts`, so `select-values.ts` must **not** import the registry — that closes a cycle and fails at module init. Inside a type's own helper module the string literal is the implementation, not a config leak.
120
120
-**The client-safe boundary.**`registry.ts` and everything it imports must stay free of `@sim/db`, `drizzle-orm`, and `next/server` — the tables grid imports it directly. A React icon is fine (it's a component *reference*, never called server-side). Only `registry.server.ts` may touch drizzle.
121
-
-**Don't re-export the registry from `@/lib/table`.**44 server modules import that barrel; routing this through it pulls `@sim/emcn/icons` into all of them. Deep-import `@/lib/table/column-types`.
121
+
-**Don't re-export the registry from `@/lib/table`.**Dozens of server modules import that barrel; routing this through it pulls `@sim/emcn/icons` into all of them. Deep-import `@/lib/table/column-types`.
122
122
-**`import.ts`'s `coerceValue` is a SECOND write path and is not opt-in.** Importing into a column of your type always hits it, and its `default` arm silently `String(value)`s — so a missing `case` stores text in a column whose `jsonbCast` is numeric, and then every filter and sort on that column errors in Postgres. Add a `case`, even though the switch compiles without one. (It is deliberately separate from the registry's `coerce`: an import wants an unparseable value to survive as its raw string so the row error can name it.)
123
123
-**CSV inference** is an ordered heuristic in `import.ts`, deliberately not registry-driven. A new type is not inferred from a CSV unless you extend `inferColumnType` — usually you should not, since inference cannot supply configuration (an option set, a currency code).
Copy file name to clipboardExpand all lines: .agents/skills/add-connector/SKILL.md
+5-4Lines changed: 5 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -469,7 +469,7 @@ The assigned mapping (`semantic id → slot`) is stored in `sourceConfig.tagSlot
469
469
470
470
## `@/connectors/utils` Helpers
471
471
472
-
Reuse these instead of inlining the same logic (the validator enforces them):
472
+
Reuse these instead of inlining the same logic:
473
473
474
474
-`htmlToPlainText(html)` — strip HTML to plain text before indexing `ExternalDocument.content`. Never index raw HTML.
475
475
-`computeContentHash(content)` — stable content hash for change detection.
@@ -538,7 +538,7 @@ If `ExternalDocument.sourceUrl` is set, the sync engine stores it on the documen
538
538
539
539
If `listDocuments` can ever return **less than the full source set** on a non-incremental sync — a `maxItems`/`maxDocuments`-style cap, or a transient per-item error that drops a still-existing document from the listing — it MUST set `syncContext.listingCapped = true` when that happens.
540
540
541
-
The sync engine reconciles deletions by comparing the full listing against stored documents: anything not seen is **hard-deleted** (sync-engine.ts, gated on `!syncContext?.listingCapped`). A truncated listing without this flag deletes every real document beyond the cap. This was the single most common bug found when auditing connectors — do not omit it.
541
+
The sync engine reconciles deletions by comparing the full listing against stored documents (`shouldReconcileDeletions` in `lib/knowledge/connectors/sync-engine.ts`, gated on `!syncContext?.listingCapped`). Anything not seen is tombstoned on that sync and hard-deleted when the next sync still does not see it — so a truncated listing without this flag eventually removes every real document beyond the cap.
542
542
543
543
```typescript
544
544
if (hitLimit&&syncContext) {
@@ -565,7 +565,7 @@ You never need to modify the sync engine when adding a connector.
565
565
566
566
## Icon
567
567
568
-
The `icon` field on `ConnectorConfig` is used throughout the UI — in the connector list, the add-connector modal, and as the document icon in the knowledge base table (replacing the generic file type icon for connector-sourced documents). The icon is read from `CONNECTOR_REGISTRY[connectorType].icon` at runtime — no separate icon map to maintain.
568
+
The `icon` field on `ConnectorConfig` is used throughout the UI — in the connector list, the add-connector modal, and as the document icon in the knowledge base table (replacing the generic file type icon for connector-sourced documents). The icon is read from `CONNECTOR_META_REGISTRY[connectorType].icon` (the client-safe registry) at runtime — no separate icon map to maintain.
569
569
570
570
If the service already has an icon in `apps/sim/components/icons.tsx` (from a tool integration), reuse it. Otherwise, ask the user to provide the SVG.
0 commit comments