Skip to content

Commit 545b498

Browse files
committed
Merge remote-tracking branch 'origin/staging' into codex/pr-6092-sailpoint-validation
# Conflicts: # apps/docs/content/docs/integrations/sailpoint.mdx # scripts/check-api-validation-contracts.ts
2 parents 531ae54 + 616d5e9 commit 545b498

13,134 files changed

Lines changed: 2261304 additions & 723706 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.agents/skills/add-block/SKILL.md‎

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ When the user asks you to create a block:
1919

2020
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.
2121

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+
2226
- Do NOT invent block outputs for undocumented tool responses
2327
- Do NOT describe unknown JSON shapes as if they were confirmed
2428
- Do NOT wire fields into the block just because they seem likely to exist
@@ -52,6 +56,12 @@ export const {ServiceName}Block: BlockConfig = {
5256
// Auth mode
5357
authMode: AuthMode.OAuth, // or AuthMode.ApiKey
5458

59+
// Card summary sentences — see "Canvas Sentences" below
60+
canvasPresentation: {
61+
defaultTitle: '{Default Operation}',
62+
sentences: { byOperation: { /* one per operation dropdown option id */ } },
63+
},
64+
5565
subBlocks: [
5666
// Define all UI fields here
5767
],
@@ -163,13 +173,41 @@ export const {ServiceName}Block: BlockConfig = {
163173

164174
Optional companions: `credentialLabels` (override the picker's section/connect-row copy) and `allowServiceAccounts: true` (trigger-mode only — list service accounts, which triggers otherwise exclude; set only when the trigger's polling path can resolve a service-account token). The connect modal, provider families (Google JSON key, Atlassian token, token-paste, client-credential, Slack bot), and the preview gate are all resolved from `serviceAccountProviderId` — you don't wire them per block.
165175

176+
### OAuth deployment availability (required for integration blocks)
177+
178+
A visible tools-category block with OAuth is deployment-gated. Its `oauth-input.serviceId` is
179+
projected into `packages/deployment-config/src/integrations.json`, then resolved through
180+
`resolveOAuthClientCapabilityId()` in `packages/deployment-config/src/env-capabilities.ts`.
181+
182+
When adding or changing an OAuth integration block:
183+
184+
1. Keep exactly one distinct OAuth `serviceId` across the block's `oauth-input` subBlocks.
185+
2. Confirm that service ID resolves to an entry in `OAUTH_CLIENT_CAPABILITIES`. Google and
186+
Microsoft service IDs intentionally share their provider-level capability; do not add duplicate
187+
entries for those aliases.
188+
3. For a new capability, add its required client fields to `OAUTH_CLIENT_CAPABILITIES` and ensure
189+
every referenced field exists in the env schema in `apps/sim/lib/core/config/env.ts`. Then add
190+
the matching `text` or `secret` input modes to `OAUTH_CLIENT_SETUP_FIELDS` in
191+
`packages/sim-setup/src/capability-config.ts`. The CLI catalog is exhaustively typed and checked
192+
against the runtime field list; do not infer secrecy from the field name.
193+
4. If the canonical OAuth service declares `serviceAccountProviderId`, run
194+
`bun run deployment-config:generate`; this regenerates the provider-ID facts in
195+
`packages/deployment-config/src/service-account-providers.generated.ts`. Never hand-edit that
196+
generated map. Add `deploymentRequirement` policy in
197+
`packages/deployment-config/src/service-account-metadata.ts` only when the service-account path
198+
is preview-gated or depends on the OAuth client fields; otherwise omit it.
199+
200+
Missing capability metadata is a runtime configuration error, not a reason to make the integration
201+
silently available.
202+
166203
### Selectors (with dynamic options)
167204
```typescript
168205
// Channel selector (Slack, Discord, etc.)
169206
{
170207
id: 'channel',
171208
title: 'Channel',
172209
type: 'channel-selector',
210+
selectorKey: '{service}.channels',
173211
serviceId: '{service}',
174212
placeholder: 'Select channel',
175213
dependsOn: ['credential'],
@@ -180,6 +218,7 @@ Optional companions: `credentialLabels` (override the picker's section/connect-r
180218
id: 'project',
181219
title: 'Project',
182220
type: 'project-selector',
221+
selectorKey: '{service}.projects',
183222
serviceId: '{service}',
184223
dependsOn: ['credential'],
185224
}
@@ -189,6 +228,7 @@ Optional companions: `credentialLabels` (override the picker's section/connect-r
189228
id: 'file',
190229
title: 'File',
191230
type: 'file-selector',
231+
selectorKey: '{service}.files',
192232
serviceId: '{service}',
193233
mimeType: 'application/pdf',
194234
dependsOn: ['credential'],
@@ -199,6 +239,7 @@ Optional companions: `credentialLabels` (override the picker's section/connect-r
199239
id: 'user',
200240
title: 'User',
201241
type: 'user-selector',
242+
selectorKey: '{service}.users',
202243
serviceId: '{service}',
203244
dependsOn: ['credential'],
204245
}
@@ -919,6 +960,62 @@ Derive templates from the service's real use cases. Each prompt should name a co
919960
- **Ground every skill in operations the block actually exposes** — cross-check each skill's steps against `tools.access`. Never describe an action the integration cannot perform.
920961
- **Derive skills from real, popular use cases found online — never invent them.** Web-search the service's documented use cases (vendor use-case/solutions pages, official docs describing the workflow, reputable "top automations for X" articles) and only add a skill you can source as something people genuinely do with the service. Do not hallucinate skills.
921962

963+
## Canvas Sentences
964+
965+
Every block declares a one-line prose summary that replaces its card's field rows:
966+
967+
```
968+
Slack ← header (already names the block)
969+
Posts ⟨Ship it 🚀⟩ to ⟨#eng⟩ ← the sentence; ⟨…⟩ are live value chips
970+
```
971+
972+
Write one `byOperation` entry per operation dropdown option (or a single `default`
973+
when the block has no operation dropdown).
974+
975+
**The full authoring contract — voice, structure, and the two mistakes that break
976+
cards silently — is `apps/sim/blocks/AGENTS.md` → "Canvas sentences". Read it
977+
before writing any.** The two failures worth repeating here, because both are
978+
invisible at runtime:
979+
980+
1. A clause naming only one member of a `canonicalParamId` pair drops the sentence
981+
for every advanced-mode user. List all members:
982+
`field: ['channelSelector', 'manualChannel']`.
983+
2. A clause referencing a subblock whose `condition` excludes that operation can
984+
never render.
985+
986+
Validate before finishing:
987+
988+
```bash
989+
bun run apps/sim/scripts/check-canvas-sentences.ts --block={service}
990+
```
991+
992+
## Generated artifacts
993+
994+
Adding a block on its own needs no **tool metadata** regeneration — a block references existing
995+
tool IDs through `tools.access` and does not change any tool's shape.
996+
997+
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`.
998+
999+
A visible integration block does require the generated integration catalog and docs to be refreshed.
1000+
After adding or changing one, run:
1001+
1002+
```bash
1003+
bun run scripts/generate-docs.ts
1004+
bun run deployment-config:generate
1005+
bun run integration-catalog:check
1006+
bun run deployment-config:check
1007+
bun run docs:check
1008+
```
1009+
1010+
The catalog check independently derives deployment metadata from the executable block registry and
1011+
compares it with the committed `packages/deployment-config/src/integrations.json`. The deployment
1012+
config check verifies the generated service-account facts against the canonical OAuth registry and
1013+
catalog. `docs:check` re-renders every generated docs artifact in memory and fails on any committed
1014+
file that differs — it runs in CI via `check:audits`, so commit the full generator output. If the
1015+
generator also trues up pages an earlier PR left stale, commit that catch-up too; reverting it as
1016+
"unrelated drift" makes `docs:check` fail. Review the generated diff and keep only intentional
1017+
changes.
1018+
9221019
## Checklist Before Finishing
9231020

9241021
- [ ] `integrationType` is set to the correct `IntegrationType` enum value
@@ -928,18 +1025,26 @@ Derive templates from the service's real use cases. Each prompt should name a co
9281025
- [ ] DependsOn set for fields that need other values
9291026
- [ ] Required fields marked correctly (boolean or condition)
9301027
- [ ] OAuth inputs have correct `serviceId` and `requiredScopes: getScopesForService(serviceId)`
1028+
- [ ] Every OAuth `serviceId` resolves through `resolveOAuthClientCapabilityId()` to the correct `OAUTH_CLIENT_CAPABILITIES` entry
1029+
- [ ] Any new OAuth capability fields exist in `apps/sim/lib/core/config/env.ts`
1030+
- [ ] If the OAuth service supports service accounts, `SERVICE_ACCOUNT_METADATA_BY_OAUTH_SERVICE_ID` matches its canonical `serviceAccountProviderId` and deployment requirement
9311031
- [ ] Scope descriptions added to `SCOPE_DESCRIPTIONS` in `lib/oauth/utils.ts` for any new scopes
9321032
- [ ] Tools.access lists all tool IDs (snake_case)
9331033
- [ ] Tools.config.tool returns correct tool ID (snake_case)
9341034
- [ ] Outputs match tool outputs
9351035
- [ ] Block + meta registered in registry-maps.ts (`BLOCK_REGISTRY` / `BLOCK_META_REGISTRY`)
1036+
- [ ] If any tool was added, changed or removed alongside the block: ran `bun run tool-metadata:generate` and committed the artifacts
1037+
- [ ] Ran `bun run scripts/generate-docs.ts`, reviewed the generated diff, and committed the integration catalog changes
1038+
- [ ] `bun run integration-catalog:check` passes
1039+
- [ ] `bun run docs:check` passes (CI gate — fails on any stale generated docs page)
9361040
- [ ] If icon missing: asked user to provide SVG
9371041
- [ ] If triggers exist: `triggers` config set, trigger subBlocks spread
9381042
- [ ] Optional/rarely-used fields set to `mode: 'advanced'`
9391043
- [ ] Timestamps and complex inputs have `wandConfig` enabled
9401044
- [ ] Exported `{Service}BlockMeta` with at least 7 templates
9411045
- [ ] `url` set on `{Service}BlockMeta` to the external service's verified homepage (omit only for first-party blocks with no external service)
9421046
- [ ] `skills` added to `{Service}BlockMeta`, each grounded in `tools.access` and sourced from a real online use case (not invented)
1047+
- [ ] `canvasPresentation.sentences` covers every operation, and `bun run apps/sim/scripts/check-canvas-sentences.ts --block={service}` passes with 100% coverage
9431048

9441049
## Final Validation (Required)
9451050

@@ -955,3 +1060,49 @@ After creating the block, you MUST validate it against every tool it references:
9551060
4. **Verify conditions** — each subBlock should only show for the operations that actually use it
9561061
5. **Verify `{Service}BlockMeta` is exported** with at least 7 templates, each having `icon`, `title`, `prompt`, `modules`, `category`, and `tags`
9571062
6. **If any tool outputs are still unknown**, explicitly tell the user instead of guessing block outputs
1063+
7. **Verify the tool execution boundary** — blocks never create or call API routes. Every referenced
1064+
tool must already be either a registered `InternalToolConfig.operation` or an absolute external
1065+
HTTP(S) `ToolConfig.request`. If transport needs to change, use the `add-tools` skill; do not add a
1066+
same-origin `/api/...` hop from the block.
1067+
1068+
## Option Lists: `selectorKey` or `options`, never a per-block fetcher
1069+
1070+
A sub-block gets its choices from exactly one of two places. There is no third.
1071+
1072+
**`selectorKey` — every remote list.** Use the `add-selector` skill to add browser-safe metadata in
1073+
`apps/sim/lib/selectors/manifest.ts`. Attach `provider-server` selectors under
1074+
`apps/sim/lib/selectors/server/providers/` and `internal-server` selectors in
1075+
`apps/sim/lib/selectors/server/internal.ts`. Point the sub-block at that key. All remote selectors
1076+
execute through `selectors.execute`; never add a client provider module or selector-only fetch route.
1077+
1078+
```ts
1079+
{ id: 'triggerCredentials', type: 'oauth-input', canonicalParamId: 'oauthCredential', mode: 'trigger' },
1080+
{ id: 'labelIds', type: 'dropdown', multiSelect: true,
1081+
selectorKey: 'gmail.labels', dependsOn: ['triggerCredentials'], mode: 'trigger' },
1082+
{ id: 'manualLabelIds', type: 'short-input', mode: 'trigger-advanced' },
1083+
```
1084+
1085+
`canonicalParamId: 'oauthCredential'` on the credential sub-block is the line people forget. The
1086+
shared context builder projects only active `dependsOn` values and keys canonical pairs by their
1087+
canonical id. Exact environment references such as `{{GMAIL_CREDENTIAL_ID}}` stay unresolved in the
1088+
browser and are resolved only by the authorized server executor. The builder does not infer a
1089+
nonstandard credential id from `type: 'oauth-input'`; give it
1090+
`canonicalParamId: 'oauthCredential'`, or declare an explicit manifest `sourceFields` alias when a
1091+
legacy source id must be retained.
1092+
1093+
**`options` — everything else.** A static array, or a pure function of the block's own values for a list that narrows to a sibling's selection. No I/O.
1094+
1095+
```ts
1096+
options: (params) => {
1097+
const model = params?.values.model
1098+
return typeof model === 'string' ? effortsFor(model) : DEFAULT_EFFORTS
1099+
}
1100+
```
1101+
1102+
**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.
1103+
1104+
Two rules the checks enforce:
1105+
1106+
- **Selector query keys contain no context values.** This includes credential IDs, raw secrets,
1107+
unresolved references, and hashes of those values; the shared facade uses an opaque local revision.
1108+
- **A sub-block that `dependsOn` a credential / knowledge-base / table selector must be reconfigurable at fork-sync time** — a `selectorKey`, a canonical pair whose basic member is a selector, or a `short-input`/`long-input`. `bun run check:fork-dependent-coverage` fails otherwise, because a fork sync clears those fields on every push and an unofferable one can never be set anywhere that sticks.

0 commit comments

Comments
 (0)