939 lines · 40.9 KB · TypeScript
RawDownload
// ---------------------------------------------------------------------------// core-tools plugin//// Built-in plugin that contributes agent-facing static tools for configuring// executor-level primitives over the v2 surface. Agent-facing connection setup// should hand users to the web UI for pasted credentials; low-level creation// through this plugin only accepts provider refs.// --------------------------------------------------------------------------- import { Effect, Schema } from "effect"; import type { Connection, ConnectionInputOrigin, CreateConnectionInput } from "./connection";import {  AuthTemplateSlug,  ConnectionName,  IntegrationSlug,  NO_AUTH_TEMPLATE,  OAuthClientSlug,  OAuthState,  ProviderItemId,  ProviderKey,  type Owner,} from "./ids";import { definePlugin, tool, type StaticToolSchema } from "./plugin";import { ToolPolicyActionSchema } from "./policies";import type { Tool } from "./tool"; const schemaToStandard = <A, I>(schema: Schema.Decoder<A, I>): StaticToolSchema<A, I> =>  Schema.toStandardSchemaV1(Schema.toStandardJSONSchemaV1(schema) as never) as StaticToolSchema<    A,    I  >; const OwnerSchema = Schema.Literals(["org", "user"]);const OAuthGrantSchema = Schema.Literals(["authorization_code", "client_credentials"]); // ---------------------------------------------------------------------------// Schemas// --------------------------------------------------------------------------- const IntegrationOutput = Schema.Struct({  slug: Schema.String,  description: Schema.String,  kind: Schema.String,  canRemove: Schema.Boolean,  canRefresh: Schema.Boolean,}); const IntegrationsListOutput = Schema.Struct({  integrations: Schema.Array(IntegrationOutput),}); const DetectInput = Schema.Struct({ url: Schema.String });const DetectOutput = Schema.Struct({  results: Schema.Array(    Schema.Struct({      kind: Schema.String,      confidence: Schema.Literals(["high", "medium", "low"]),      endpoint: Schema.String,      name: Schema.String,      slug: Schema.String,    }),  ),}); const ConnectionOutput = Schema.Struct({  owner: OwnerSchema,  name: Schema.String,  integration: Schema.String,  template: Schema.String,  provider: Schema.String,  address: Schema.String,  identityLabel: Schema.optional(Schema.NullOr(Schema.String)),  description: Schema.optional(Schema.NullOr(Schema.String)),  expiresAt: Schema.NullOr(Schema.Number),  oauthClient: Schema.NullOr(Schema.String),  oauthClientOwner: Schema.NullOr(OwnerSchema),  oauthScope: Schema.NullOr(Schema.String),}); const ConnectionsListInput = Schema.Struct({  integration: Schema.optional(Schema.String),  owner: Schema.optional(OwnerSchema),  verbose: Schema.optional(Schema.Boolean),}); /** Lean per-connection shape for list scans. Omits the full `oauthScope` *  grant string (a single connection's scope list can run to thousands of *  characters and dominates the payload) in favor of `oauthScopeCount`. The *  full scope is included only when the caller passes `verbose: true`. */const ConnectionListItem = Schema.Struct({  owner: OwnerSchema,  name: Schema.String,  integration: Schema.String,  template: Schema.String,  provider: Schema.String,  address: Schema.String,  identityLabel: Schema.optional(Schema.NullOr(Schema.String)),  description: Schema.optional(Schema.NullOr(Schema.String)),  expiresAt: Schema.NullOr(Schema.Number),  oauthClient: Schema.NullOr(Schema.String),  oauthClientOwner: Schema.NullOr(OwnerSchema),  oauthScopeCount: Schema.NullOr(Schema.Number),  oauthScope: Schema.optional(Schema.NullOr(Schema.String)),});const ConnectionsListOutput = Schema.Struct({  connections: Schema.Array(ConnectionListItem),}); const ConnectionCreateHandoffInput = Schema.Struct({  integration: Schema.String,  owner: Schema.optional(OwnerSchema),  template: Schema.optional(Schema.String),  label: Schema.optional(Schema.String),});const ConnectionCreateHandoffOutput = Schema.Struct({  url: Schema.String,  instructions: Schema.String,}); const ConnectionFromInput = Schema.Struct({  provider: Schema.String,  id: Schema.String,});const ConnectionInputOriginInput = Schema.Struct({ from: ConnectionFromInput });const ConnectionCreateInput = Schema.Struct({  owner: OwnerSchema,  name: Schema.String,  integration: Schema.String,  template: Schema.String,  identityLabel: Schema.optional(Schema.NullOr(Schema.String)),  from: Schema.optional(ConnectionFromInput),  inputs: Schema.optional(Schema.Record(Schema.String, ConnectionInputOriginInput)),}).check(  Schema.makeFilter((payload) => {    const originCount =      (payload.from === undefined ? 0 : 1) + (payload.inputs === undefined ? 0 : 1);    // The no-auth template ("none") binds zero credentials — both `from` and    // `inputs` are legitimately absent (public MCP servers, public REST APIs).    // Mirror the engine, which accepts an empty input set only for this    // template; a stray origin would wire a credential the connection can't    // hold, so reject any. Every other template needs exactly one origin.    const isNoAuth = String(payload.template) === String(NO_AUTH_TEMPLATE);    if (isNoAuth) {      if (originCount > 0) {        return 'A no-auth connection (template "none") takes no provider credential origin';      }    } else if (originCount !== 1) {      return "Expected exactly one provider credential origin";    }    if (payload.inputs !== undefined && Object.keys(payload.inputs).length === 0) {      return "Expected at least one provider credential input";    }    return undefined;  }),);const ConnectionRefInput = Schema.Struct({  owner: OwnerSchema,  name: Schema.String,  integration: Schema.String,}); const ToolOutput = Schema.Struct({  address: Schema.String,  owner: OwnerSchema,  integration: Schema.String,  connection: Schema.String,  name: Schema.String,  pluginId: Schema.String,  description: Schema.String,});const ConnectionsRefreshOutput = Schema.Struct({  tools: Schema.Array(ToolOutput),}); const RemovedOutput = Schema.Struct({ removed: Schema.Boolean });const CancelledOutput = Schema.Struct({ cancelled: Schema.Boolean }); const ProvidersOutput = Schema.Struct({  providers: Schema.Array(Schema.String),}); const ProviderItemsInput = Schema.Struct({ provider: Schema.String });const ProviderItemsOutput = Schema.Struct({  items: Schema.Array(Schema.Struct({ id: Schema.String, name: Schema.String })),}); const PolicyOutput = Schema.Struct({  id: Schema.String,  owner: OwnerSchema,  pattern: Schema.String,  action: Schema.String,  position: Schema.String,});const PoliciesListOutput = Schema.Struct({  policies: Schema.Array(PolicyOutput),}); const PolicyCreateInput = Schema.Struct({  owner: OwnerSchema,  pattern: Schema.String,  action: ToolPolicyActionSchema,});const PolicyUpdateInput = Schema.Struct({  id: Schema.String,  owner: OwnerSchema,  pattern: Schema.optional(Schema.String),  action: Schema.optional(ToolPolicyActionSchema),});const PolicyRemoveInput = Schema.Struct({  id: Schema.String,  owner: OwnerSchema,}); const OAuthClientOutput = Schema.Struct({  owner: OwnerSchema,  slug: Schema.String,  grant: OAuthGrantSchema,  authorizationUrl: Schema.String,  tokenUrl: Schema.String,  resource: Schema.optional(Schema.NullOr(Schema.String)),  clientId: Schema.String,  origin: Schema.Union([    Schema.Struct({ kind: Schema.Literal("manual") }),    Schema.Struct({      kind: Schema.Literal("dynamic_client_registration"),      integration: Schema.optional(Schema.NullOr(Schema.String)),    }),  ]),});const OAuthClientsListOutput = Schema.Struct({  clients: Schema.Array(OAuthClientOutput),});// No `clientSecret`: a confidential client's secret must never cross the agent// boundary (it would land in the LLM context window). This tool registers a// PUBLIC client only; a secret-bearing app is registered by the human through// `oauth.clients.createHandoff`, which deep-links them to the web form.const OAuthCreateClientInput = Schema.Struct({  owner: OwnerSchema,  slug: Schema.String,  authorizationUrl: Schema.String,  tokenUrl: Schema.String,  grant: OAuthGrantSchema,  clientId: Schema.String,  resource: Schema.optional(Schema.NullOr(Schema.String)),});// Browser-handoff for a CONFIDENTIAL OAuth app: carries only the NON-secret// fields the form pre-fills. The client secret is typed by the human in the web// UI, exactly like a pasted connection credential, so it never reaches the// agent. Mirrors `ConnectionCreateHandoffInput`.const OAuthCreateClientHandoffInput = Schema.Struct({  integration: Schema.String,  owner: Schema.optional(OwnerSchema),  slug: Schema.optional(Schema.String),  grant: Schema.optional(OAuthGrantSchema),  clientId: Schema.optional(Schema.String),  authorizationUrl: Schema.optional(Schema.String),  tokenUrl: Schema.optional(Schema.String),  resource: Schema.optional(Schema.NullOr(Schema.String)),  label: Schema.optional(Schema.String),});const OAuthCreateClientHandoffOutput = Schema.Struct({  url: Schema.String,  instructions: Schema.String,});const OAuthClientOutputRef = Schema.Struct({  client: Schema.String,});const OAuthRegisterDynamicInput = Schema.Struct({  owner: OwnerSchema,  slug: Schema.String,  registrationEndpoint: Schema.String,  authorizationUrl: Schema.String,  tokenUrl: Schema.String,  resource: Schema.optional(Schema.NullOr(Schema.String)),  scopes: Schema.Array(Schema.String),  tokenEndpointAuthMethodsSupported: Schema.optional(Schema.Array(Schema.String)),  clientName: Schema.optional(Schema.String),  redirectUri: Schema.optional(Schema.NullOr(Schema.String)),  originIntegration: Schema.optional(Schema.NullOr(Schema.String)),});const OAuthRemoveClientInput = Schema.Struct({  owner: OwnerSchema,  slug: Schema.String,});const OAuthProbeInput = Schema.Struct({  url: Schema.String,});const OAuthProbeOutput = Schema.Struct({  authorizationUrl: Schema.String,  tokenUrl: Schema.String,  resource: Schema.optional(Schema.NullOr(Schema.String)),  scopesSupported: Schema.optional(Schema.Array(Schema.String)),  registrationEndpoint: Schema.optional(Schema.NullOr(Schema.String)),  tokenEndpointAuthMethodsSupported: Schema.optional(Schema.Array(Schema.String)),  clientIdMetadataDocumentSupported: Schema.optional(Schema.Boolean),});const OAuthStartInput = Schema.Struct({  client: Schema.String,  clientOwner: OwnerSchema,  owner: OwnerSchema,  name: Schema.String,  integration: Schema.String,  template: Schema.String,  identityLabel: Schema.optional(Schema.NullOr(Schema.String)),  redirectUri: Schema.optional(Schema.NullOr(Schema.String)),});const OAuthStartOutput = Schema.Union([  Schema.Struct({    status: Schema.Literal("connected"),    connection: ConnectionOutput,  }),  Schema.Struct({    status: Schema.Literal("redirect"),    authorizationUrl: Schema.String,    state: Schema.String,  }),]);const OAuthCancelInput = Schema.Struct({  state: Schema.String,}); // Standard-schema versions for the tool() builder.const IntegrationsListOutputStd = schemaToStandard(IntegrationsListOutput);const DetectInputStd = schemaToStandard(DetectInput);const DetectOutputStd = schemaToStandard(DetectOutput);const ConnectionsListInputStd = schemaToStandard(ConnectionsListInput);const ConnectionsListOutputStd = schemaToStandard(ConnectionsListOutput);const ConnectionCreateHandoffInputStd = schemaToStandard(ConnectionCreateHandoffInput);const ConnectionCreateHandoffOutputStd = schemaToStandard(ConnectionCreateHandoffOutput);const ConnectionCreateInputStd = schemaToStandard(ConnectionCreateInput);const ConnectionOutputStd = schemaToStandard(ConnectionOutput);const ConnectionRefInputStd = schemaToStandard(ConnectionRefInput);const ConnectionsRefreshOutputStd = schemaToStandard(ConnectionsRefreshOutput);const RemovedOutputStd = schemaToStandard(RemovedOutput);const CancelledOutputStd = schemaToStandard(CancelledOutput);const ProvidersOutputStd = schemaToStandard(ProvidersOutput);const ProviderItemsInputStd = schemaToStandard(ProviderItemsInput);const ProviderItemsOutputStd = schemaToStandard(ProviderItemsOutput);const PoliciesListOutputStd = schemaToStandard(PoliciesListOutput);const PolicyOutputStd = schemaToStandard(PolicyOutput);const PolicyCreateInputStd = schemaToStandard(PolicyCreateInput);const PolicyUpdateInputStd = schemaToStandard(PolicyUpdateInput);const PolicyRemoveInputStd = schemaToStandard(PolicyRemoveInput);const OAuthClientsListOutputStd = schemaToStandard(OAuthClientsListOutput);const OAuthCreateClientInputStd = schemaToStandard(OAuthCreateClientInput);const OAuthCreateClientHandoffInputStd = schemaToStandard(OAuthCreateClientHandoffInput);const OAuthCreateClientHandoffOutputStd = schemaToStandard(OAuthCreateClientHandoffOutput);const OAuthClientOutputRefStd = schemaToStandard(OAuthClientOutputRef);const OAuthRegisterDynamicInputStd = schemaToStandard(OAuthRegisterDynamicInput);const OAuthRemoveClientInputStd = schemaToStandard(OAuthRemoveClientInput);const OAuthProbeInputStd = schemaToStandard(OAuthProbeInput);const OAuthProbeOutputStd = schemaToStandard(OAuthProbeOutput);const OAuthStartInputStd = schemaToStandard(OAuthStartInput);const OAuthStartOutputStd = schemaToStandard(OAuthStartOutput);const OAuthCancelInputStd = schemaToStandard(OAuthCancelInput); const connectionToOutput = (connection: Connection) => ({  owner: connection.owner,  name: String(connection.name),  integration: String(connection.integration),  template: String(connection.template),  provider: String(connection.provider),  address: String(connection.address),  identityLabel: connection.identityLabel ?? null,  description: connection.description ?? null,  expiresAt: connection.expiresAt ?? null,  oauthClient: connection.oauthClient == null ? null : String(connection.oauthClient),  oauthClientOwner: connection.oauthClientOwner ?? null,  oauthScope: connection.oauthScope ?? null,}); /** Number of space-separated grants in an `oauthScope` string, or null when *  the connection carries no scope (static credentials, or an OAuth AS that *  omitted scope). */const oauthScopeCount = (scope: string | null | undefined): number | null =>  scope == null ? null : scope.split(/\s+/).filter(Boolean).length; /** Lean projection for `connections.list`. Summarizes `oauthScope` to a count *  unless `verbose`, where the full grant string is included too. */const connectionToListItem = (connection: Connection, verbose: boolean) => ({  owner: connection.owner,  name: String(connection.name),  integration: String(connection.integration),  template: String(connection.template),  provider: String(connection.provider),  address: String(connection.address),  identityLabel: connection.identityLabel ?? null,  description: connection.description ?? null,  expiresAt: connection.expiresAt ?? null,  oauthClient: connection.oauthClient == null ? null : String(connection.oauthClient),  oauthClientOwner: connection.oauthClientOwner ?? null,  oauthScopeCount: oauthScopeCount(connection.oauthScope),  ...(verbose ? { oauthScope: connection.oauthScope ?? null } : {}),}); const toolToOutput = (toolRow: Tool) => ({  address: String(toolRow.address),  owner: toolRow.owner,  integration: String(toolRow.integration),  connection: String(toolRow.connection),  name: String(toolRow.name),  pluginId: toolRow.pluginId,  description: toolRow.description,}); const connectionRefFromInput = (input: typeof ConnectionRefInput.Type) => ({  owner: input.owner as Owner,  integration: IntegrationSlug.make(input.integration),  name: ConnectionName.make(input.name),}); const originFromInput = (  origin: typeof ConnectionInputOriginInput.Type,): ConnectionInputOrigin => ({  from: {    provider: ProviderKey.make(origin.from.provider),    id: ProviderItemId.make(origin.from.id),  },}); const createConnectionInputFromTool = (  input: typeof ConnectionCreateInput.Type,): CreateConnectionInput => {  const base = {    owner: input.owner as Owner,    name: ConnectionName.make(input.name),    integration: IntegrationSlug.make(input.integration),    template: AuthTemplateSlug.make(input.template),    identityLabel: input.identityLabel ?? null,  };   if (input.from !== undefined) {    return {      ...base,      from: {        provider: ProviderKey.make(input.from.provider),        id: ProviderItemId.make(input.from.id),      },    };  }  return {    ...base,    inputs: Object.fromEntries(      Object.entries(input.inputs ?? {}).map(([variable, origin]) => [        variable,        originFromInput(origin),      ]),    ),  };}; const connectionCreateHandoffUrl = (  webBaseUrl: string | undefined,  orgSlug: string | undefined,  input: typeof ConnectionCreateHandoffInput.Type,): string => {  const search = new URLSearchParams({ addAccount: "1" });  if (input.owner !== undefined) search.set("owner", input.owner);  if (input.template !== undefined) search.set("template", input.template);  if (input.label !== undefined) search.set("label", input.label);  // Org-scoped hosts (cloud, self-host, cloudflare) serve the console under an  // optional `/<org-slug>` segment. Pin the URL to the executor's bound org so  // it opens that org directly instead of relying on the browser's last-active  // org (which `OrgSlugGate` would otherwise canonicalize a bare URL to). When  // no slug is known (CLI, local, non-request callers) we emit the bare path.  const orgPrefix = orgSlug !== undefined && orgSlug.length > 0 ? `/${orgSlug}` : "";  const path = `${orgPrefix}/integrations/${encodeURIComponent(input.integration)}?${search.toString()}`;  if (webBaseUrl === undefined || webBaseUrl.length === 0) return path;  return new URL(path, webBaseUrl.endsWith("/") ? webBaseUrl : `${webBaseUrl}/`).toString();}; const oauthClientCreateHandoffUrl = (  webBaseUrl: string | undefined,  orgSlug: string | undefined,  input: typeof OAuthCreateClientHandoffInput.Type,): string => {  // `oauthClient=1` flips the integration's Add-account flow straight into the  // Register-OAuth-app form; the rest pre-fill its NON-secret fields. The client  // secret is deliberately absent: the human types it in the browser, so it is  // never placed in this URL (nor in the agent's context). Same builder shape as  // `connectionCreateHandoffUrl`.  const search = new URLSearchParams({ addAccount: "1", oauthClient: "1" });  if (input.owner !== undefined) search.set("owner", input.owner);  if (input.slug !== undefined) search.set("clientSlug", input.slug);  if (input.grant !== undefined) search.set("grant", input.grant);  if (input.clientId !== undefined) search.set("clientId", input.clientId);  if (input.authorizationUrl !== undefined) search.set("authorizationUrl", input.authorizationUrl);  if (input.tokenUrl !== undefined) search.set("tokenUrl", input.tokenUrl);  if (input.resource != null && input.resource.length > 0) search.set("resource", input.resource);  if (input.label !== undefined) search.set("label", input.label);  const orgPrefix = orgSlug !== undefined && orgSlug.length > 0 ? `/${orgSlug}` : "";  const path = `${orgPrefix}/integrations/${encodeURIComponent(input.integration)}?${search.toString()}`;  if (webBaseUrl === undefined || webBaseUrl.length === 0) return path;  return new URL(path, webBaseUrl.endsWith("/") ? webBaseUrl : `${webBaseUrl}/`).toString();}; export interface CoreToolsPluginOptions {  readonly webBaseUrl?: string;  /** The bound org's URL slug, prefixed onto browser-handoff URLs so they open

Lines 1–500 of 939.