Scrydon

Référence SDK

Référence API complète pour le SDK @scrydon/sdk-authoring/extensions/authoring

Installation

bun add -d @scrydon/sdk-authoring zod

Chemins d'importation

Le SDK utilise des exports de sous-chemins explicites — il n'existe pas d'import barrel racine.

// Helpers de définition (toutes les fonctions define*)
import {
  defineExtension, defineToolkit, defineTool, defineBlock, defineTriggerBlock,
  defineCapabilityLLM, defineCapabilitySTT, defineCapabilityTTS,
  defineCapabilityEmbedding, defineCapabilityVideo, defineCapabilityOCR,
  defineCapabilityWebhook, defineCapabilityDiscovery,
} from "@scrydon/sdk-authoring/extensions/authoring/define";

// Types de runtime (utilisés dans les signatures execute())
import type {
  PureContext, PureContextAuth, ToolResponse, ExecutorResult,
  RealtimeSession, RealtimeMessage,
} from "@scrydon/sdk-authoring/extensions/authoring/context";

// Helpers de filtrage des credentials (lèvent une CredentialKindError en cas de type incompatible)
import {
  requireOAuthToken, requireOAuth,
  requireApiKey, requireBasicAuth, requireBotToken,
} from "@scrydon/sdk-authoring/extensions/authoring/context";

// Types du manifeste (pour les cas d'usage avancés)
import type { ManifestSchema } from "@scrydon/sdk-authoring/extensions/authoring/manifest";

defineExtension(config)

Conteneur de niveau supérieur pour une intégration. L'export par défaut du point d'entrée doit être le résultat d'un appel à defineExtension().

interface VendorInput {
  /** Identifiant unique du fournisseur — alphanumérique minuscule + tirets, commence par une lettre */
  id: string;
  /** Nom d'affichage */
  name: string;
  /** Version sémantique (ex. "1.0.0") */
  version: string;
  /** Description courte */
  description?: string;
  /** Couleur de marque en hexadécimal (ex. "#FF6B35") */
  color?: string;
  /** Icône SVG sous forme de chaîne */
  icon: string;
  /** URL du site web du fournisseur */
  website?: string;
  /** URL de la documentation */
  docsUrl?: string;
  /** Catégories (ex. ["tools"], ["llm"], ["database"]) */
  categories?: string[];
  /** Indique si ce fournisseur est activé par défaut pour les nouvelles organisations */
  defaultEnabled?: boolean;
  /** Indique si ce fournisseur prend en charge le mode système (client_credentials) */
  supportsSystemMode?: boolean;
  /** Indique si ce fournisseur nécessite une connectivité réseau externe */
  connectivity?: "cloud" | "local" | "hybrid";
  /** Isolation minimale demandée. Les minimums plateforme et organisation ne peuvent que la renforcer. */
  isolation?: "memory" | "microvm";
  /** Hôtes HTTPS publics que le code d'outil peut contacter — exacts ou wildcards `*.`. */
  toolEgress?: {
    httpsHosts: string[];
  };
  /** Configuration de l'authentification */
  auth: AuthConfig;
  /** Champs configurables par l'administrateur (ex. URL de base pour les services auto-hébergés) */
  configFields?: ConfigFieldInput[];
  /** Secrets au niveau de l'extension (résolus par invocation via le Dapr Secret Store) */
  secrets?: SecretInput[];
  /** Ce que fournit l'extension — au moins un toolkit, une famille de modèles ou un type de contenu */
  provides: {
    toolkits?: ToolkitDefinition[];
    models?: ModelFamilyDefinition[];
  };
  /** Définitions de protocoles optionnelles */
  protocols?: ProtocolDefinition[];
}

Isolation et egress des outils

isolation est une demande minimale, pas un sélecteur de backend. Les outils Scrydon exacts et révisés peuvent utiliser le backend memory approuvé. Tout autre outil livré ou installé par une organisation a un minimum microvm imposé par la plateforme, même si son manifeste demande memory ou omet le champ. Une politique d'organisation peut renforcer un outil Scrydon révisé vers microvm ; aucun réglage ne peut affaiblir le minimum de la plateforme. Si le backend Runtime Plane attesté est indisponible, l'outil refuse de s'exécuter.

Déclarez chaque nom d'hôte HTTPS public qu'un outil peut contacter :

export default defineExtension({
  // ...
  toolEgress: {
    httpsHosts: ["api.acme.example", "uploads.acme.example"],
  },
});

Les hôtes doivent être des noms DNS en minuscules, sans schéma, chemin ni port.

Lorsque votre hôte d'API dépend du locataire, préfixez une entrée par *. :

export default defineExtension({
  // ...
  toolEgress: {
    // Couvre acme.my.acme-crm.example et acme--dev.sandbox.my.acme-crm.example
    httpsHosts: ["*.my.acme-crm.example"],
  },
});

Une entrée *. correspond aux sous-domaines à n'importe quelle profondeur mais pas à son propre apex — déclarez l'apex séparément si vos outils le contactent. Le wildcard doit couvrir au moins deux labels : *.example est donc refusé, aucun manifeste ne pouvant revendiquer un TLD entier. Le préfixe *. est la seule forme de wildcard ; un segment variable à l'intérieur d'un label (*-api.acme.example) n'est pas exprimable — déclarez alors le domaine parent. Préférez des hôtes exacts partout où l'ensemble des destinations est fixe : un wildcard sert aux hôtes que vous ne pouvez réellement pas connaître avant l'exécution.

À l'exécution, la plateforme intersecte cette liste avec la politique d'egress de l'organisation ; un wildcard n'élargit jamais ce que l'organisation autorise. Les sockets Bun/Node directs, ports alternatifs, adresses privées, rebinding DNS et sorties par redirection sont refusés à la frontière de la microVM vendeur. La DLP sortante analyse la requête matérialisée qui quitterait la VM, pas seulement les arguments de l'outil.

Configuration de l'authentification

Le champ auth définit les types de credentials pris en charge par votre fournisseur :

auth: {
  credentials: {
    none: { type: "none" },
  },
  default: "none",
}

Vous pouvez définir plusieurs types de credentials et laisser l'utilisateur choisir :

auth: {
  credentials: {
    oauth: { type: "oauth", /* ... */ },
    apiKey: { type: "apiKey", /* ... */ },
  },
  default: "oauth", // Lequel afficher en premier
}

Champs de configuration

Pour les fournisseurs nécessitant des paramètres configurés par l'administrateur (ex. une URL de base auto-hébergée) :

configFields: [
  {
    key: "baseUrl",
    label: "Base URL",
    placeholder: "https://your-instance.example.com",
    description: "The base URL of your self-hosted instance",
    required: true,
    type: "url",
  },
],

Secrets

Pour les secrets au niveau du fournisseur stockés dans le Dapr Secret Store :

secrets: [
  {
    key: "WEBHOOK_SIGNING_SECRET",
    label: "Webhook Signing Secret",
    description: "Used to verify incoming webhook payloads",
    required: true,
  },
],

Les secrets sont référencés à l'exécution via ctx.secrets["WEBHOOK_SIGNING_SECRET"]. Dans le chemin gouverné d'outil en microVM vendeur, les chaînes de credential et de secret sont des placeholders limités à l'invocation. Transmettez-les sans modification à ctx.fetch ; le broker d'egress de confiance remplace la valeur réelle uniquement dans un emplacement de requête autorisé. Ne les inspectez, transformez, persistez, journalisez ou retournez jamais.


defineToolkit(config)

Regroupe des outils, des déclencheurs et un bloc sous un toolkit pouvant être activé ou désactivé par organisation. Les runtimes de modèles ne se déclarent PAS ici : ils appartiennent à une famille de modèles, voir defineModels() ci-dessous.

interface ToolkitInput {
  /** Identifiant unique de l'élément fourni au sein de l'extension */
  id: string;
  /** Nom d'affichage */
  name: string;
  /** Description courte */
  description?: string;
  /** SVG en ligne (utilise le logo de l'extension si absent) */
  logo?: string;
  /** Référence au credential d'auth.credentials utilisé par ce toolkit */
  credentialRef?: string;
  /** Scopes OAuth requis par ce toolkit */
  credentialScopes?: string[];
  /** Outils exécutables */
  tools?: ToolDefinition[];
  /** Déclencheurs — affichés dans la section « Déclencheurs » de l'éditeur */
  triggers?: TriggerDefinition[];
  /** Bloc principal du toolkit sur le canevas (absent pour un toolkit sans bloc) */
  block?: BlockDefinition;
  webhooks?: WebhookCapability;
  discovery?: DiscoveryCapability;
  webSearch?: WebSearchCapability;
  /** Catalogue de ressources ; chaque action déclare séparément ses cibles */
  resources?: Array<{
    resourceType: string;
    displayName: string;
    description?: string;
    allowMultiple?: boolean;
  }>;
  /** Dépendances NPM (pour l'évaluation des risques au moment de l'upload) */
  dependencies?: Array<{
    type: "npm";
    package: string;
    version: string;
    reason?: string;
  }>;

Le tableau dependencies sert de contexte lisible par un humain — chaque entrée inclut un reason expliquant pourquoi la dépendance est nécessaire. La CLI génère automatiquement un SBOM lisible par machine (meta/sbom.cdx.json) qui capture l'ensemble complet des paquets intégrés avec leurs versions et licences. Les deux sont complémentaires : les dépendances déclarées expliquent l'intention, le SBOM fournit l'inventaire auditables.

  /** Configuration du mode système (client_credentials) */
  systemMode?: {
    supported: boolean;
    permissions?: string[];
  };
}

Extensions de consentement système

Les credentials OAuth peuvent déclarer des points d'extension sûrs pour le manifeste afin de gérer des flux de configuration spécifiques au fournisseur, comme le consentement administrateur Microsoft. Cette logique doit rester dans l'intégration : le manifeste doit décrire la forme de l'URL du fournisseur, les champs de configuration de l'organisation requis, et les paramètres de rappel (callback).

Pour la plupart des fournisseurs, implémentez le consentement système via un modèle de redirection déclaratif. C'est la voie recommandée lorsque la plateforme n'a besoin que de construire une URL à partir de valeurs de runtime :

auth: {
  credentials: {
    oauth: {
      type: "oauth",
      label: "Microsoft Account",
      authorizationUrl: "https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/authorize",
      tokenUrl: "https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token",
      requiredConfig: ["tenantId"],
      supportsClientCredentials: true,
      clientCredentialScopes: ["https://graph.microsoft.com/.default"],
      extensions: {
        systemConsent: {
          kind: "redirect-template",
          urlTemplate: "https://login.microsoftonline.com/{tenantId}/adminconsent",
          params: {
            client_id: "{clientId}",
            redirect_uri: "{redirectUri}",
            scope: "{scope}",
            state: "{state}",
          },
          requiredConfig: ["tenantId"],
          callback: {
            successParam: "admin_consent",
            tenantParam: "tenant",
          },
        },
      },
    },
  },
}

La plateforme substitue ces espaces réservés :

Espace réservéValeur
{clientId}ID client OAuth résolu pour l'organisation
{redirectUri}URL de rappel de consentement administrateur de la plateforme
{scope}clientCredentialScopes joints par un espace
{state}État CSRF généré par la plateforme
Toute clé requiredConfig, comme {tenantId}Valeur de configuration du fournisseur pour l'organisation

Si un fournisseur ne peut pas être représenté sous forme de modèle d'URL, déclarez une référence de gestionnaire dans le manifeste et implémentez la méthode référencée dans le runtime du fournisseur. La méthode doit être pure : accepter des valeurs de runtime résolues, valider toute configuration spécifique au fournisseur requise, et retourner l'URL de redirection plutôt que de coder en dur la logique du fournisseur dans la plateforme.

extensions: {
  systemConsent: {
    kind: "handler",
    handlerRef: "microsoft.systemConsent.buildUrl",
    callback: {
      handlerRef: "microsoft.systemConsent.callback",
    },
  },
}
type SystemConsentBuildUrlInput = {
  clientId: string;
  redirectUri: string;
  state?: string;
  scopes: string[];
  providerConfig: Record<string, string | undefined>;
};

export async function buildUrl(input: SystemConsentBuildUrlInput): Promise<string> {
  const tenantId = input.providerConfig.tenantId;
  if (!tenantId) {
    throw new Error("Microsoft admin consent requires tenantId");
  }

  const url = new URL(`https://login.microsoftonline.com/${tenantId}/adminconsent`);
  url.searchParams.set("client_id", input.clientId);
  url.searchParams.set("redirect_uri", input.redirectUri);
  url.searchParams.set("scope", input.scopes.join(" "));

  if (input.state) {
    url.searchParams.set("state", input.state);
  }

  return url.toString();
}

type SystemConsentCallbackInput = {
  query: Record<string, string | undefined>;
  providerConfig: Record<string, string | undefined>;
};

export async function callback(input: SystemConsentCallbackInput) {
  return {
    approved: input.query.admin_consent === "True",
    tenantId: input.query.tenant,
  };
}

Les références de gestionnaire sont des métadonnées du manifeste. Les archives uploadés doivent aujourd'hui utiliser kind: "redirect-template", sauf si l'application hôte a explicitement câblé l'exécution du runtime pour le gestionnaire référencé.

Extensions multi-toolkits

Les extensions comme Google ou Microsoft fournissent plusieurs toolkits. Chaque toolkit dispose de son propre bloc dans l'éditeur de workflow et peut être activé indépendamment :

export default defineExtension({
  id: "acme",
  // ...
  provides: {
    toolkits: [
      defineToolkit({
        id: "acme-crm",
        name: "Acme CRM",
        tools: [listContacts, createContact],
        block: crmBlock,
        triggers: [],
      }),
      defineToolkit({
        id: "acme-email",
        name: "Acme Email",
        tools: [sendEmail, listInbox],
        block: emailBlock,
        triggers: [],
      }),
    ],
  },
});

defineTool(config)

Définit la logique de runtime d'un outil. C'est ici que se font vos appels API réels, transformations de données ou calculs.

interface ToolInput<TInput, TOutput> {
  /** Identifiant unique de l'outil — convention : {vendor}:{product}:{action} */
  id: string;
  /** Nom d'affichage */
  name: string;
  /** Version sémantique */
  version: string;
  /** Description affichée aux utilisateurs et aux agents LLM */
  description?: string;
  /** Schéma Zod pour la validation de l'entrée */
  input: z.ZodType<TInput>;
  /** Schéma Zod pour la validation de la sortie */
  output: z.ZodType<TOutput>;
  /** Métadonnées des paramètres — contrôle la visibilité dans l'interface et auprès des LLM */
  params?: Record<string, ParamDef>;
  /** Ressources exactes du produit autorisées avant l'exécution de l'action */
  resourceAuthorization?: Array<{
    required?: boolean;
    oneOf: Array<{
      resourceType: string;
      source: { kind: "input"; key: string } | { kind: "binding" };
    }>;
  }>;
  /** La fonction exécutée lors de l'invocation de cet outil */
  execute: (input: TInput, ctx: PureContext) => Promise<ToolResponse<TOutput>>;
}

interface ParamDef {
  type: string;
  required?: boolean;
  description?: string;
  default?: unknown;
  /** Contrôle l'endroit où ce paramètre apparaît */
  visibility?: "user-only" | "user-or-llm" | "hidden";
}

product.resources est un catalogue, pas une exigence d'exécution globale. Déclarez la cible réelle de chaque action avec resourceAuthorization. Les règles provenant d'une entrée sont obligatoires ; seule une règle provenant du binding peut utiliser required: false pour conserver le comportement délégué à la racine du compte, par exemple /me. Les cibles déléguées sont vérifiées en direct auprès du fournisseur ; les cibles app/système exigent une attribution exacte du même produit, type et identifiant de ressource dans l'espace de travail.

Visibilité des paramètres

VisibilitéQui peut fournir la valeurCas d'usage
user-or-llmL'utilisateur la définit dans l'interface, ou un agent IA la génèreLa plupart des paramètres
user-onlySeul l'utilisateur peut la configurer (masquée du LLM)Paramètres de connexion, credentials
hiddenNon affichée dans l'interface ni au LLM — définie par programmationIdentifiants internes, valeurs calculées

Fonction d'exécution

La fonction execute reçoit l'entrée validée et un PureContext :

import { requireOAuthToken } from "@scrydon/sdk-authoring/extensions/authoring/context";

async execute(input, ctx) {
  // Utiliser le logger (visible dans les journaux d'exécution du workflow)
  ctx.logger.info(`Processing request for org ${ctx.execution.orgId}`);

  // Filtrer sur ctx.auth.kind avant de lire les champs de credential.
  // Cet exemple suppose que le fournisseur déclare un credential OAuth —
  // requireOAuthToken lève une CredentialKindError si le type ne correspond pas.
  const accessToken = requireOAuthToken(ctx.auth);

  // Effectuer des appels API authentifiés
  const response = await fetch("https://api.example.com/data", {
    headers: {
      Authorization: `Bearer ${accessToken}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify(input),
  });

  if (!response.ok) {
    return {
      success: false,
      output: { error: `API returned ${response.status}` },
      error: `HTTP ${response.status}: ${response.statusText}`,
    };
  }

  const data = await response.json();
  return {
    success: true,
    output: data,
    metadata: { requestId: response.headers.get("x-request-id") },
  };
},

PureContext

L'interface PureContext est la seule interface entre la plateforme et le code de l'archive. Elle est injectée dans chaque appel à execute() au moment de l'exécution :

/**
 * Union discriminée de toutes les formes de credentials que la plateforme peut injecter.
 * Filtrez toujours sur `kind` avant de lire les champs de credential.
 * Importez les helpers de filtrage depuis @scrydon/sdk-authoring/extensions/authoring/context :
 *   requireOAuthToken(auth) → string (accessToken, lève une erreur en cas d'incompatibilité)
 *   requireOAuth(auth)      → { accessToken, refreshToken?, tokenType?, expiresAt? }
 *   requireApiKey(auth)     → string (apiKey)
 *   requireBasicAuth(auth)  → { username, password }
 *   requireBotToken(auth)   → string (botToken)
 * Chaque helper lève une CredentialKindError avec un message actionnable en cas d'incompatibilité.
 */
type PureContextAuth =
  | { kind: "oauth"; accessToken: string; refreshToken?: string; tokenType?: string; expiresAt?: number }
  | { kind: "apiKey"; apiKey: string }
  | { kind: "basicAuth"; username: string; password: string }
  | { kind: "botToken"; botToken: string }
  | { kind: "none" };

interface PureContext {
  /** Credential résolu par la plateforme avant l'invocation */
  auth: PureContextAuth;
  /** Métadonnées d'exécution pour la journalisation et la corrélation */
  execution: {
    executionId: string;      // Identifiant unique de l'exécution
    workflowId?: string;      // Identifiant du workflow (si exécuté dans un workflow)
    nodeId?: string;          // Identifiant du nœud de bloc dans le workflow
    orgId: string;            // Identifiant de l'organisation
    workspaceId?: string;     // Identifiant de l'espace de travail
    userId?: string;          // Utilisateur qui a déclenché l'exécution
  };
  /** Logger structuré — les appels console sont interceptés dans le sandbox */
  logger: {
    info(message: string, ...args: unknown[]): void;
    warn(message: string, ...args: unknown[]): void;
    error(message: string, ...args: unknown[]): void;
    debug(message: string, ...args: unknown[]): void;
  };
  /** Secrets du fournisseur configurés par l'administrateur de l'organisation (résolus par invocation via le Dapr Secret Store) */
  secrets: Record<string, string>;
  /** Présent lors de l'exécution via un Profil d'intégration — contient les remplacements spécifiques au profil */
  profileConfig?: Record<string, unknown>;
}

PureContext est importé depuis @scrydon/sdk-authoring/extensions/authoring/context. La plateforme le construit avant chaque invocation d'outil — votre code n'a jamais besoin d'en créer un manuellement. Tous les champs sauf auth et execution peuvent être des objets vides si non applicables. auth est une union discriminée (PureContextAuth) ; filtrez toujours sur auth.kind ou utilisez un helper require* avant d'accéder aux champs spécifiques au credential. Consultez Modes d'authentification pour la référence complète de la forme par mode.

ToolResponse

Chaque execute() doit retourner un ToolResponse :

interface ToolResponse<T = unknown> {
  /** Indique si l'exécution de l'outil a réussi */
  success: boolean;
  /** Les données de sortie typées */
  output: T;
  /** Message d'erreur (affiché à l'utilisateur si success est false) */
  error?: string;
  /** Métadonnées optionnelles (journalisées mais non transmises aux blocs en aval) */
  metadata?: Record<string, unknown>;
}

defineBlock(config)

Définit l'interface utilisateur de l'éditeur de workflow — comment le bloc apparaît, quels champs de formulaire il contient et comment les données le traversent.

interface BlockInput {
  /** Type de bloc unique — minuscule avec underscores (ex. "acme_crm") */
  type: string;
  /** Nom d'affichage dans la palette de blocs */
  name: string;
  /** Description courte */
  description?: string;
  /** Catégorie du bloc — utiliser "extension" pour les blocs de archive */
  category: string;
  /** Couleur de fond en hexadécimal */
  bgColor?: string;
  /** Mode d'auth : "none", "apiKey", "oauth", "botToken" */
  authMode?: string;
  /** Champs de formulaire affichés dans le panneau du bloc */
  subBlocks?: SubBlockInput[];
  /** Les outils que ce bloc peut invoquer */
  tools?: {
    access: string[];
    config?: {
      /** Sélection dynamique d'outil en fonction de la saisie de l'utilisateur */
      tool: (params: Record<string, any>) => string;
      /** Transformation des paramètres avant transmission à l'outil */
      params?: (params: Record<string, any>) => any;
    };
  };
  /** Connexions d'entrée depuis les blocs en amont */
  inputs?: Record<string, { type: string; description?: string }>;
  /** Connexions de sortie vers les blocs en aval */
  outputs?: Record<string, { type: string; description?: string }>;
  /** Configuration du déclencheur (pour les blocs déclencheurs) */
  triggers?: { enabled: boolean; available: string[] };
}

Types de SubBlock

Les SubBlocks sont les champs de formulaire qui apparaissent lorsqu'un utilisateur clique sur un bloc :

TypeDescriptionPropriétés clés
short-inputChamp de texte sur une ligneplaceholder, password, defaultValue, generatedDefault, readOnly, showCopyButton
long-inputZone de texte multiligneplaceholder
codeÉditeur de code avec coloration syntaxiqueplaceholder, mode
dropdownMenu de sélectionoptions: [{ label, id }]
comboboxListe déroulante avec rechercheoptions, fetchOptions
sliderCurseur de plagemin, max, step
switchInterrupteur à basculedefaultValue
oauth-inputSélecteur de credential OAuthserviceId, requiredScopes

Définition de SubBlock

interface SubBlockInput {
  /** Identifiant unique du champ dans le bloc */
  id: string;
  /** Libellé du champ */
  title?: string;
  /** Type de SubBlock */
  type: string;
  /** Disposition : "full" (par défaut) ou "half" */
  layout?: "full" | "half";
  /** Indique si ce champ est obligatoire */
  required?: boolean;
  /** Texte d'espace réservé */
  placeholder?: string;
  /** Masquer la saisie en points de mot de passe */
  password?: boolean;
  /** Valeur par défaut */
  defaultValue?: unknown;
  /** Générer une nouvelle valeur une fois lors de la création de chaque bloc */
  generatedDefault?: "uuid";
  /** Options pour liste déroulante/combobox */
  options?: Array<{ label: string; id: string }>;
  /** Texte d'aide affiché sous le champ */
  description?: string;
  /** Visibilité conditionnelle basée sur la valeur d'un autre champ */
  condition?: { field: string; value: string };
  /** Indique si le champ est en lecture seule */
  readOnly?: boolean;
  /** Afficher un bouton de copie dans le presse-papiers */
  showCopyButton?: boolean;
  /** Masquer ce champ dans l'interface */
  hidden?: boolean;
}

Utilisez generatedDefault: "uuid" lorsque chaque nouveau bloc doit recevoir sa propre valeur initiale, par exemple un secret de webhook. Ce marqueur déclaratif traverse la frontière JSON du manifeste d'intégration ; l'UUID généré est enregistré comme valeur ordinaire du champ et n'est pas régénéré au chargement du workflow.

Sélection dynamique d'outil

Lorsqu'un bloc dispose de plusieurs outils, utilisez tools.config.tool pour sélectionner lequel s'exécute :

defineBlock({
  // ...
  subBlocks: [
    {
      id: "operation",
      title: "Operation",
      type: "dropdown",
      options: [
        { label: "Create", id: "create" },
        { label: "Read", id: "read" },
        { label: "Update", id: "update" },
        { label: "Delete", id: "delete" },
      ],
    },
    // ... autres champs
  ],
  tools: {
    access: ["acme_create", "acme_read", "acme_update", "acme_delete"],
    config: {
      tool: (params) => `acme_${params.operation}`,
    },
  },
});

Visibilité conditionnelle

Afficher ou masquer des champs en fonction des valeurs d'autres champs :

subBlocks: [
  {
    id: "authType",
    title: "Auth Type",
    type: "dropdown",
    options: [
      { label: "API Key", id: "apiKey" },
      { label: "Username/Password", id: "basic" },
    ],
  },
  {
    id: "apiKey",
    title: "API Key",
    type: "short-input",
    password: true,
    condition: { field: "authType", value: "apiKey" },
  },
  {
    id: "username",
    title: "Username",
    type: "short-input",
    condition: { field: "authType", value: "basic" },
  },
  {
    id: "password",
    title: "Password",
    type: "short-input",
    password: true,
    condition: { field: "authType", value: "basic" },
  },
],

defineTriggerBlock(config)

Définit un bloc déclencheur — un membre de workflow à part entière qui s'affiche dans la section latérale « Triggers » et porte des métadonnées de runtime décrivant comment il se déclenche (webhook, polling, ou les deux). La valeur de category est automatiquement définie à "trigger".

interface TriggerBlockInput {
  /** Type de bloc unique */
  type: string;
  /** Nom d'affichage */
  name: string;
  /** Description */
  description?: string;
  /** Couleur de fond en hexadécimal */
  bgColor?: string;
  /** Mode d'auth : "none", "apiKey", "oauth", "botToken" */
  authMode?: string;
  /** Champs de formulaire pour la configuration du déclencheur */
  subBlocks?: SubBlockInput[];
  /** Les outils que ce bloc déclencheur peut invoquer */
  tools?: {
    access: string[];
    config?: {
      tool?: (params: Record<string, any>) => string;
      params?: (params: Record<string, any>) => any;
    };
  };
  /** Connexions d'entrée */
  inputs?: Record<string, { type: string; description?: string }>;
  /** Schéma de sortie — données émises par le déclencheur */
  outputs?: Record<string, { type: string; description?: string }>;
  /** Identifiant du fournisseur */
  provider?: string;
  /** Version sémantique */
  version?: string;
  /** Configuration de runtime — comment les événements atteignent le moteur de workflow */
  runtime?: {
    webhook?: {
      method?: "POST" | "GET" | "PUT" | "DELETE";
      headers?: Record<string, string>;
    };
    polling?: PollingProvider;
  };
}

Gestion des secrets

Les fournisseurs peuvent déclarer des secrets chiffrés au repos et résolus par invocation. Les secrets sont limités à l'organisation et au fournisseur, de sorte que chaque organisation dispose de son propre jeu de credentials isolé.

Déclaration des secrets

Définissez les exigences en matière de secrets dans defineExtension() via le tableau secrets :

export default defineExtension({
  id: "acme",
  name: "Acme",
  version: "1.0.0",
  logo: acmeIcon,
  auth: { credentials: { apiKey: { type: "apiKey", label: "API Key", identityScope: "application" } }, default: "apiKey" },
  secrets: [
    {
      key: "WEBHOOK_SIGNING_SECRET",
      label: "Webhook Signing Secret",
      description: "Used to verify incoming webhook payloads",
      required: true,
    },
    {
      key: "ENCRYPTION_KEY",
      label: "Encryption Key",
      description: "AES key for encrypting sensitive payloads",
      required: false,
    },
  ],
  provides: { toolkits: [/* ... */] },
});

Chaque SecretInput a la forme suivante :

interface SecretInput {
  /** Clé unique au sein du fournisseur — doit correspondre à ^[a-zA-Z0-9_-]+$ */
  key: string;
  /** Libellé lisible affiché dans l'interface d'administration */
  label: string;
  /** Texte d'aide décrivant l'utilisation de ce secret */
  description?: string;
  /** Indique si l'intégration nécessite ce secret pour fonctionner */
  required: boolean;
}

Les clés de secret ne peuvent contenir que des caractères alphanumériques, des tirets et des underscores (^[a-zA-Z0-9_-]+$). Les clés ne correspondant pas à ce format sont rejetées lors de l'enregistrement.

Format des clés de secret

Les secrets sont stockés avec une clé structurée qui les rattache à l'organisation et au fournisseur :

extension:{orgId}:{extensionId}:{key}

Par exemple, un secret avec la clé WEBHOOK_SIGNING_SECRET pour le fournisseur acme dans l'organisation org_abc123 est stocké sous :

extension:org_abc123:acme:WEBHOOK_SIGNING_SECRET

Cela garantit une isolation complète entre les organisations et entre les fournisseurs au sein d'une même organisation.

Chiffrement au repos

Toutes les valeurs de secret sont chiffrées avec AES-256-GCM avant d'être persistées. La stratégie de clé de chiffrement est configurée par organisation — LOCAL, BYOK ou HYOK. Les valeurs de secret ne sont jamais stockées en clair. Consultez Sécurité → Gestion des secrets pour le modèle complet.

Accès aux secrets au moment de l'exécution

ctx.secrets est un Record<string, string> plat contenant les clés configurées. Dans le chemin gouverné en microVM vendeur, chaque valeur est un placeholder limité à l'invocation et adossé à un grant scellé du broker :

import { defineTool } from "@scrydon/sdk-authoring/extensions/authoring/define";
import { z } from "zod";

export const verifyWebhook = defineTool({
  id: "acme:webhooks:verify",
  name: "Verify Webhook",
  version: "1.0.0",
  input: z.object({ payload: z.string(), signature: z.string() }),
  output: z.object({ valid: z.boolean() }),
  async execute(input, ctx) {
    const signingSecret = ctx.secrets["WEBHOOK_SIGNING_SECRET"];
    if (!signingSecret) {
      return { success: false, output: { valid: false }, error: "Webhook signing secret not configured" };
    }

    const response = await ctx.fetch("https://api.acme.example/webhooks/verify", {
      method: "POST",
      headers: {
        "content-type": "application/json",
        "x-acme-signing-secret": signingSecret,
      },
      body: JSON.stringify(input),
    });
    return { success: true, output: { valid: response.ok } };
  },
});

Transmettez les placeholders sans modification à ctx.fetch. Le broker de confiance remplace la valeur réutilisable après l'autorisation de destination et avant que la DLP sortante analyse la requête réelle. Le guest, le système de fichiers, le modèle, le plan, les logs, les traces et la réponse de l'outil ne reçoivent jamais la valeur réutilisable.

L'objet ne contient que les clés définies par l'administrateur de l'organisation ; une valeur absente reste absente. Les opérations locales qui doivent inspecter ou transformer un secret réutilisable, comme une signature HMAC dans le code d'un outil Agent non-Scrydon, sont intentionnellement indisponibles sur cette frontière.

Interface d'administration

Les administrateurs d'organisation configurent les secrets sous Paramètres > Plateforme > Extensions, dans la fiche du fournisseur. L'interface affiche les champs de secret déclarés dans le tableau secrets du fournisseur.

Les valeurs de secret sont en écriture seule — une fois une valeur enregistrée, elle n'est plus jamais affichée. L'interface indique uniquement quelles clés ont été configurées (avec un indicateur masqué) et permet aux administrateurs de les mettre à jour ou de les supprimer.

Points de terminaison API

La plateforme expose trois points de terminaison internes pour la gestion des secrets :

Point de terminaisonObjectif
/extensions/authoring/admin/archive-secret-setCréer ou mettre à jour une valeur de secret
/extensions/authoring/admin/archive-secret-deleteSupprimer un secret
/extensions/authoring/admin/archive-secret-listLister les clés de secret configurées (les valeurs ne sont jamais retournées)

Ces points de terminaison sont internes (service à service) et nécessitent que l'appelant soit un service interne autorisé. Ils ne sont pas directement accessibles depuis les applications clientes.


CLI de build

# Construire une archive
sdk-authoring extension build [--entry src/index.ts] [--outDir dist]

# Tester une archive
sdk-authoring extension test [--level static|sandbox|live] [--tool <id>]

Résultat du build

my-vendor-1.0.0.archive.tar.gz
├── manifest.json           # Métadonnées auto-générées (JSON Schemas, définitions UI)
├── dist/
│   └── index.js            # ESM compilé et minifié (toutes les dépendances intégrées via esbuild)
├── meta/
│   ├── sbom.cdx.json       # SBOM CycloneDX 1.6 (auto-généré)
│   └── metafile.json       # Graphe de dépendances esbuild
└── assets/                 # Icônes optionnelles
    └── icon.svg

La CLI de build intègre toutes les dépendances npm dans le fichier dist/index.js unique et génère automatiquement un SBOM CycloneDX 1.6 dans meta/sbom.cdx.json. Il n'y a pas de npm install au moment de l'exécution — l'archive est entièrement autonome et déterministe.

Règles des identifiants de fournisseur

  • Caractères alphanumériques minuscules et tirets uniquement
  • Doit commencer par une lettre
  • Format : /^[a-z][a-z0-9-]*$/
  • Exemples : acme, my-vendor, hello-world

Convention des identifiants d'outil

Les identifiants d'outil utilisent le format complet {extensionId}:{providedId}:{action} au moment de l'exécution :

  • hello-world:hello-world:say-hello
  • google:sheets:read
  • slack:messaging:send-message

Forme abrégée : Dans le code d'authoring, vous pouvez utiliser uniquement la partie action (ex. "say-hello", "read"). Le SDK qualifie automatiquement les identifiants courts en ajoutant {extensionId}:{providedId}: lors de l'appel à defineExtension(). Toutes les intégrations natives utilisent la forme courte. Si vous utilisez la forme complète, le préfixe vendor/product doit correspondre — la CLI de build valide cela.


Helpers de capacités

Les helpers de capacités encapsulent les configurations de runtime avec une balise de type afin que l'extracteur de manifeste puisse les identifier. Chaque helper retourne sa configuration avec une balise _capability utilisée en interne lors du build.

HelperBaliseObjectif
defineCapabilityLLM(config)llmFournisseur LLM complet (chat, complétion, streaming)
defineCapabilitySTT(config)sttReconnaissance vocale (batch et temps réel)
defineCapabilityTTS(config)ttsSynthèse vocale (batch et temps réel)
defineCapabilityEmbedding(config)embeddingGénération d'embeddings vectoriels
defineCapabilityVideo(config)videoGénération vidéo
defineCapabilityOCR(config)ocrAnalyse de documents / reconnaissance optique de caractères
defineCapabilityWebhook(config)webhookGestion des webhooks spécifiques au fournisseur
defineCapabilityDiscovery(config)discoveryNavigation dans les ressources, sélecteurs, portées d'accès

Tous les helpers sont importés depuis @scrydon/sdk-authoring/extensions/authoring/define :

import {
  defineCapabilityLLM,
  defineCapabilitySTT,
  defineCapabilityTTS,
  defineCapabilityEmbedding,
  defineCapabilityVideo,
  defineCapabilityOCR,
  defineCapabilityWebhook,
  defineCapabilityDiscovery,
} from "@scrydon/sdk-authoring/extensions/authoring/define";

Les configurations de capacités de modèle sont placées dans defineModels({ family: "<famille>", runtime: { ... } }), une famille par entrée de provides.models ; webSearch est en revanche un membre du toolkit. L'extracteur de manifeste convertit les fonctions de runtime en indicateurs booléens et extrait les métadonnées des modèles (dimensions, benchmarks, tarification) dans le champ capabilityMeta du manifeste. Consultez Capacités pour des exemples d'utilisation complets de chaque helper.


Schéma du manifeste

La CLI de build génère automatiquement manifest.json à partir de votre appel à defineExtension(). Le manifeste est un instantané sérialisable en JSON des métadonnées de votre fournisseur, sans les fonctions de runtime. Il suit l'unique forme prise en charge manifestVersion: 1. Toute autre version est rejetée.

Structure de niveau supérieur

{
  "$schema": "...",              // URL optionnelle du JSON Schema
  "manifestVersion": 1,         // Toujours 1
  "version": "1.0.0",           // Version du fournisseur (issue de defineExtension)
  "vendor": { ... },            // Métadonnées du fournisseur
  "auth": { ... },              // Configuration de l'authentification
  "products": [ ... ],          // Définitions des produits
  "protocols": [ ... ],         // Définitions de protocoles (optionnel)
  "llm": { ... }                // Configuration du fournisseur LLM (optionnel)
}

Section fournisseur

Contient les métadonnées d'affichage. Le code de runtime est exclu.

ChampTypeObligatoireDescription
idstringOuiIdentifiant du fournisseur (/^[a-z][a-z0-9-]*$/)
namestringOuiNom d'affichage
descriptionstringNonDescription courte
colorstringNonCouleur de marque en hexadécimal
iconstringOuiIcône SVG sous forme de chaîne
websitestringNonURL du site web du fournisseur
docsUrlstringNonURL de la documentation
categoriesstring[]NonÉtiquettes de catégorie
defaultEnabledbooleanNonActivation automatique pour les nouvelles organisations (par défaut : false)
supportsSystemModebooleanNonPrend en charge client_credentials (par défaut : false)
connectivity"cloud" | "local" | "hybrid"NonExigences réseau
configFieldsConfigField[]NonChamps configurables par l'administrateur
secretsSecret[]NonSecrets au niveau du fournisseur

Section produit

Chaque produit dans le tableau products contient :

ChampTypeObligatoireDescription
idstringOuiIdentifiant du produit
namestringOuiNom d'affichage
capabilities.toolsTool[]NonDéfinitions d'outils avec JSON Schema entrée/sortie
capabilities.triggersTrigger[]NonDéfinitions de déclencheurs
capabilities.runtimesobjectNonIndicateurs booléens pour llm, stt, tts, embedding, video, ocr
capabilities.capabilityMetaobjectNonMétadonnées de modèles extraites (dimensions d'embedding, modèles STT/TTS, benchmarks)
capabilities.webhooksbooleanNonIndique si le produit gère les webhooks
capabilities.discoverybooleanNonIndique si le produit prend en charge la découverte de ressources
workflow.blockBlockNonDéfinition du bloc de l'éditeur de workflow
workflow.triggersTriggerBlock[]NonBlocs déclencheurs du workflow
resourcesResource[]NonCatalogue de ressources, jamais une règle globale pour toutes les actions
dependenciesDependency[]NonDépendances NPM déclarées avec leurs raisons
systemMode.permissionsstring[]NonPermissions de consentement app/système

Comment le code de runtime devient des données de manifeste

La CLI de build transforme les définitions de runtime en JSON sûr pour le manifeste :

Source (defineToolkit)Sortie du manifeste
runtimes.llm (avec fonction executeRequest)runtimes.llm: true
runtimes.embedding (avec fonction embed, modèles avec dimension)runtimes.embedding: true + capabilityMeta.embeddingModels
runtimes.stt (avec fonction transcribe, modèles)runtimes.stt: true + capabilityMeta.sttModels
runtimes.tts (avec fonction synthesize, modèles)runtimes.tts: true + capabilityMeta.ttsModels
input/output d'un outil (schémas Zod)Objets JSON Schema
Fonction execute d'un outilOmise (non sérialisable)
Fonction tools.config.tool d'un blocOmise (sérialisée séparément)

Le manifeste est lu au démarrage pour alimenter le catalogue d'intégrations. Il ne contient aucun code exécutable, uniquement des métadonnées, des JSON Schemas et des définitions UI. L'archive exécutable est chargé à la demande par le backend canonique. Les capacités et outils approuvés peuvent utiliser le backend de compatibilité Worker Thread ; un outil statique non-Scrydon sélectionné par un Agent isolé exige la microVM fournisseur épinglée à la source et validée par preuve.

Sessions continues de transcription

Une extension STT qui déclare des modèles en streaming doit implémenter createStreamingSession(request) et openStreamingSession(request, context). La compilation vérifie cette association. streaming.protocol est un identifiant propre à l’extension : ajouter un fournisseur ne nécessite aucun adaptateur dans l’application d’enregistrement.

Les types SttSession, SttSessionContext et SttStreamingSessionRequest sont exportés par @scrydon/sdk-authoring/extensions/capabilities/stt-streaming. SttSession expose sendAudio(Uint8Array), finalize(), close(), onTranscript(handler), onError(handler) et onClose(handler). L’audio reçu est du PCM16 mono continu à 16 kHz. L’extension gère l’authentification, le rééchantillonnage, le protocole, le renouvellement des identifiants et le SDK du fournisseur. Utilisez les interfaces fetch et WebSocket contrôlées par le sandbox ; les modules réseau natifs ne peuvent pas contourner ces contrôles. Les champs context.auth, context.secrets et context.profileConfig reprennent le contexte standard des extensions ; l’authentification n’est pas limitée aux clés API. Respectez context.signal, libérez les ressources à la fermeture et traitez tout l’audio accepté avant de terminer finalize().

Les résultats ont la forme { text, isFinal, startMs?, endMs?, confidence?, speakerId? }. Les temps sont exprimés en millisecondes depuis le début audio de la session. N’inventez pas de temps ni d’attribution manquants. Les identifiants de locuteur ne sont valables que dans la session continue concernée ; ils n’identifient pas un membre de l’organisation ni une personne dans une autre requête STT.

La plateforme fournit aux clients média une autorisation scrydon-stt-v1. Les endpoints et identifiants du fournisseur restent dans l’extension, y compris les valeurs direct et proxied de createStreamingSession.

Les sessions continues nécessitent actuellement du code d’extension revu, admis sur le backend worker. Si la politique exige un processus géré ou une microVM, le streaming est refusé avant l’émission du ticket : ces backends prennent en charge les appels requête/réponse. La transcription par lots conserve ses niveaux d’exécution habituels. Ne réduisez pas les exigences d’isolation pour contourner cette limite.

Lors de la mise à niveau, recompilez et réinstallez les extensions de streaming avec le SDK d’authoring mis à jour. Les anciens bundles qui émettent uniquement des tickets ne possèdent pas le nouveau hook de session. Déployez ensemble la plateforme et le client média : celui-ci exige scrydon-stt-v1 et ne rétablit pas les adaptateurs fournisseur dans l’application.

Modèles vocaux OpenAI de première partie

L’extension OpenAI utilise le même contrat de session que les autres extensions. Configurez sa connexion par clé API OpenAI, puis choisissez un modèle pour la capacité STT :

ModèleEntréeHorodatage et locuteurs
gpt-live-transcribeSession en directLimites des tours via VAD serveur ; aucun horodatage par mot ni locuteur fourni
gpt-transcribeFichier / énoncéTexte et langues détectées ; aucun horodatage inventé
gpt-4o-transcribe, gpt-4o-mini-transcribeFichier / énoncéTexte sans horodatage par mot
gpt-4o-transcribe-diarizeFichier / énoncéSegments horodatés avec locuteurs propres à la requête
whisper-1Fichier / énoncéHorodatage par mot ou segment ; traduction vers l’anglais

Le modèle par défaut reste whisper-1. Les modèles en direct et sur fichier sont séparés : un modèle réservé au direct ne peut pas traiter la transcription finale d’un fichier. L’extension utilise le socket contrôlé par la plateforme, convertit le PCM de 16 kHz à 24 kHz dans chaque session et attend les transcriptions finales avant de terminer finalize(). Elle déclare la limite fournisseur de 60 minutes pour le mécanisme de rotation existant. Le suivi des locuteurs reste indépendant du STT ; les labels d’une requête ne constituent pas des identités entre requêtes.

Les fichiers conservent leur format audio, y compris les fenêtres FLAC de Recording Studio, et sont limités à 25 Mo. La diarisation utilise diarized_json avec chunking_strategy=auto, sans prompt ni option d’horodatage par mot non pris en charge. L’accès aux modèles dépend du compte OpenAI configuré. Reconstruisez et réinstallez l’extension OpenAI pour disposer des nouveaux modèles et du contrat de session.

Références fournisseur : transcription de fichiers, transcription en direct et cycle de vie des sessions.

Sur cette page

Sur cette page