import { Effect, Inspectable, Layer, Option, Predicate, Schema } from "effect";import { FetchHttpClient, type HttpClient } from "effect/unstable/http";import { fumadb } from "@executor-js/fumadb";import { memoryAdapter } from "@executor-js/fumadb/adapters/memory";import { withQueryContext, type Condition, type ConditionBuilder } from "@executor-js/fumadb/query";import { schema as fumaSchema, type RelationsMap } from "@executor-js/fumadb/schema";import type { AnyColumn } from "@executor-js/fumadb/schema";import { StorageError, afterCommit, isStorageFailure, makeFumaClient, type FumaDb, type FumaRow, type FumaTables, type StorageFailure,} from "./fuma-runtime";import { makeFumaBlobStore, pluginBlobStore, type BlobStore, type OwnerPartitions } from "./blob";import { makePendingApprovalStore, type PendingApprovalStore } from "./pending-approval";import { coreToolsPlugin } from "./core-tools";import type { Connection, ConnectionInputOrigin, ConnectionRef, CreateConnectionInput, ConnectionValueInput, UpdateConnectionInput, ValidateConnectionInput,} from "./connection";import { HealthCheckResult, HealthCheckSpec } from "./health-check";import type { HealthCheckCandidate } from "./health-check";import { ARTIFACT_SUMMARY_COLUMNS, coreSchema, isToolPolicyAction, TOOL_INVOCATION_COLUMNS, type ConnectionRow, type CoreSchema, type IntegrationRow, type OAuthClientRow, type ToolInvocationRow, type ToolRow, type ToolPolicyRow,} from "./core-schema";import { ElicitationDeclinedError, ElicitationResponse, FormElicitation, type ElicitationHandler, type ElicitationRequest, type OnElicitation, type InvokeOptions,} from "./elicitation"; export type { OnElicitation, InvokeOptions } from "./elicitation";import { rowToArtifact, rowToArtifactSummary, type Artifact, type ArtifactSummary, type RemoveArtifactInput, type RenameArtifactInput, type SaveArtifactInput, type SetArtifactPreviewInput,} from "./artifact";import { ArtifactNotFoundError, ConnectionNotFoundError, CredentialProviderNotRegisteredError, CredentialResolutionError, IntegrationNotFoundError, InvalidConnectionInputError, IntegrationRemovalNotAllowedError, NoHandlerError, PluginNotLoadedError, ToolBlockedError, ToolInvocationError, ToolNotFoundError, type ExecuteError,} from "./errors";import { ArtifactId, AuthTemplateSlug, ConnectionAddress, ConnectionName, IntegrationSlug, NO_AUTH_TEMPLATE, OAuthClientSlug, Owner, PolicyId, ProviderItemId, ProviderKey, Subject, Tenant, ToolAddress, ToolName,} from "./ids";import type { AuthMethodDescriptor, Integration, IntegrationChangeEvent, IntegrationConfig, IntegrationDisplayDescriptor, RegisterIntegrationInput,} from "./integration";import { makeOAuthService, type MintOAuthConnectionInput, type OAuthScopePolicy,} from "./oauth-service";import type { OAuthService } from "./oauth-client";import { comparePolicyRow, isValidPattern, matchPattern, positionForNewPattern, resolveEffectivePolicy, rowToToolPolicy, type CreateToolPolicyInput, type EffectivePolicy, type RemoveToolPolicyInput, type ToolPolicy, type UpdateToolPolicyInput,} from "./policies";import type { CredentialProvider, ProviderEntry } from "./provider";import { touchSubject } from "./subject-registry";import type { AnyPlugin, Elicit, IntegrationConfigureSchema, IntegrationPresetCatalogEntry, IntegrationRecord, OwnerBinding, PluginCtx, PluginExtensions, ResolveToolsResult, StaticIntegrationDecl, StaticToolDecl, StorageDeps, ToolPolicyProvider, ToolPolicyProviderRule, ToolInvocationCredential,} from "./plugin";import { pluginStorageId, type PluginStorageCollectionData, type PluginStorageCollectionDefinition, type PluginStorageCollectionQueryInput, type PluginStorageEntry, type PluginStorageFacade, type PluginStorageRuntimeCollectionDefinition, type PluginStorageRuntimeIndexSpec,} from "./plugin-storage";import { assertExecutorOwnerPolicyTable, ORG_SUBJECT, type ExecutorOwnerPolicyContext,} from "./owner-policy";import { ToolSchemaView, type IntegrationDetectionResult } from "./types";import { type Tool, type ToolAnnotations, type ToolDef, type ToolListFilter } from "./tool";import { buildToolTypeScriptPreview } from "./schema-types";import { collectReferencedDefinitions } from "./schema-refs";import { refreshAccessToken, exchangeClientCredentials, shouldRefreshToken, type OAuthEndpointUrlPolicy,} from "./oauth-helpers";import { connectionIdentifier } from "./connection-name-identifier";import { annotateToolResultOutcome } from "./tool-result";import { isUnauthorizedToolFailure } from "./auth-tool-failure"; const PLUGIN_STORAGE_DELETE_KEY_BATCH_SIZE = 90;const PLUGIN_STORAGE_CREATE_ROW_BATCH_SIZE = 90;const MAX_APPROVAL_ARGUMENT_PREVIEW_CHARS = 4_000; // ---------------------------------------------------------------------------// Elicitation handler — resolved once at `createExecutor({ onElicitation })`// and overridable per `execute`. A tool that requests user input mid-execution// suspends the fiber and the handler decides how to respond. The "accept-all"// sentinel auto-accepts (tests / non-interactive hosts).// --------------------------------------------------------------------------- const acceptAllHandler: ElicitationHandler = () => Effect.succeed(ElicitationResponse.make({ action: "accept" })); const resolveElicitationHandler = (onElicitation: OnElicitation): ElicitationHandler => onElicitation === "accept-all" ? acceptAllHandler : onElicitation; // ---------------------------------------------------------------------------// Address scheme — `tools.<integration>.<owner>.<connection>.<tool>`.// --------------------------------------------------------------------------- const ADDRESS_PREFIX = "tools"; export interface ParsedToolAddress { readonly integration: IntegrationSlug; readonly owner: Owner; readonly connection: ConnectionName; readonly tool: ToolName;} const isOwner = (value: string): value is Owner => value === "org" || value === "user"; /** Parse a callable address; null when it's not a well-formed * `tools.<integration>.<owner>.<connection>.<tool>`. * * The four leading segments (prefix, integration, owner, connection) are * slug-like and never contain a `.`; the `<tool>` segment is the *entire* * remainder after the 4th dot, so it may itself contain dots. That lets a tool * whose name is a structured `group.leaf` path (e.g. an OpenAPI * `aliases.deleteAlias`) address naturally as * `tools.<integration>.<owner>.<connection>.aliases.deleteAlias` — the same * dotted nesting the sandbox `tools` proxy produces from property access. */export const parseToolAddress = (address: string): ParsedToolAddress | null => { // Walk to the 4th dot; everything past it is the tool (dots and all). let cut = -1; for (let i = 0; i < 4; i++) { cut = address.indexOf(".", cut + 1); if (cut === -1) return null; } const [prefix, integration, owner, connection] = address.slice(0, cut).split(".") as [ string, string, string, string, ]; const tool = address.slice(cut + 1); if (prefix !== ADDRESS_PREFIX) return null; if (!isOwner(owner)) return null; if (integration.length === 0 || connection.length === 0 || tool.length === 0) { return null; } return { integration: IntegrationSlug.make(integration), owner, connection: ConnectionName.make(connection), tool: ToolName.make(tool), };}; export const connectionAddress = ( owner: Owner, integration: IntegrationSlug, connection: ConnectionName,): ConnectionAddress => ConnectionAddress.make(`${ADDRESS_PREFIX}.${integration}.${owner}.${connection}`); export const toolAddress = ( owner: Owner, integration: IntegrationSlug, connection: ConnectionName, tool: ToolName,): ToolAddress => ToolAddress.make(`${ADDRESS_PREFIX}.${integration}.${owner}.${connection}.${tool}`); // ---------------------------------------------------------------------------// Owner key helpers — every owned-row write stamps `tenant`, `owner`,// `subject` (org → subject="").// --------------------------------------------------------------------------- interface OwnedKeys { readonly tenant: string; readonly owner: Owner; readonly subject: string;} // ---------------------------------------------------------------------------// Executor — public surface. Every list/execute/schema call is a direct// core-table query unioned with the in-memory static pool.// --------------------------------------------------------------------------- export type Executor<TPlugins extends readonly AnyPlugin[] = readonly []> = { readonly integrations: { readonly list: () => Effect.Effect<readonly Integration[], StorageFailure>; readonly get: (slug: IntegrationSlug) => Effect.Effect<Integration | null, StorageFailure>; readonly update: ( slug: IntegrationSlug, patch: { readonly name?: string; readonly description?: string }, ) => Effect.Effect<void, IntegrationNotFoundError | StorageFailure>; readonly remove: ( slug: IntegrationSlug, ) => Effect.Effect<void, IntegrationRemovalNotAllowedError | StorageFailure>; readonly detect: ( url: string, ) => Effect.Effect<readonly IntegrationDetectionResult[], StorageFailure>; /** The integration's declared health check: which authenticated operation a * connection runs to prove its credential is alive and surface whose * account it is. Configured by the user the same way auth methods are. */ readonly healthCheck: { /** The currently declared check, or null when none is configured (or the * owning plugin has no health-check capability). */ readonly get: ( slug: IntegrationSlug, ) => Effect.Effect<HealthCheckSpec | null, StorageFailure>; /** The operations a user can pick from, ranked non-destructive-first then * fewest required arguments. Empty when the plugin has no candidates. */ readonly candidates: ( slug: IntegrationSlug, ) => Effect.Effect< readonly HealthCheckCandidate[], IntegrationNotFoundError | StorageFailure >; /** Declare (or clear, with `null`) the health check for the integration. */ readonly set: ( slug: IntegrationSlug, spec: HealthCheckSpec | null, ) => Effect.Effect<void, IntegrationNotFoundError | StorageFailure>; }; }; readonly connections: { readonly create: ( input: CreateConnectionInput, ) => Effect.Effect< Connection, | IntegrationNotFoundError | CredentialProviderNotRegisteredError | InvalidConnectionInputError | StorageFailure >; readonly list: (filter?: { readonly integration?: IntegrationSlug; readonly owner?: Owner; }) => Effect.Effect<readonly Connection[], StorageFailure>; readonly get: (ref: ConnectionRef) => Effect.Effect<Connection | null, StorageFailure>; /** Edit user-curated metadata (description, identityLabel). Credentials and * OAuth lifecycle fields are not editable here. */ readonly update: ( ref: ConnectionRef, input: UpdateConnectionInput, ) => Effect.Effect<Connection, ConnectionNotFoundError | StorageFailure>; readonly remove: ( ref: ConnectionRef, ) => Effect.Effect<void, ConnectionNotFoundError | StorageFailure>; readonly refresh: ( ref: ConnectionRef, ) => Effect.Effect< readonly Tool[], ConnectionNotFoundError | IntegrationNotFoundError | StorageFailure >; /** Run the integration's declared health check against a saved connection: * classify the credential (healthy / expired / degraded / unknown) and * extract its identity for display. Never throws on an auth wall or upstream * error: those come back as a `HealthCheckResult` with the matching status. */ readonly checkHealth: ( ref: ConnectionRef, options?: { /** Return the persisted verdict when younger than this; probe * otherwise. Omit to always probe. */ readonly ifStaleMs?: number; }, ) => Effect.Effect< HealthCheckResult, ConnectionNotFoundError | IntegrationNotFoundError | StorageFailure >; /** Validate an in-flight credential WITHOUT saving it (key-first connect): * resolve the pasted value(s), run the health check, and return the result * so the caller can confirm the key works and derive a name from the * identity before creating the connection. */ readonly validate: ( input: ValidateConnectionInput, ) => Effect.Effect<HealthCheckResult, IntegrationNotFoundError | StorageFailure>; }; /** Shared OAuth service. Hosts use this through the core HTTP OAuth group; * plugins see the same service as `ctx.oauth`. */ readonly oauth: OAuthService; readonly tools: { readonly list: (filter?: ToolListFilter) => Effect.Effect<readonly Tool[], StorageFailure>; readonly schema: (address: ToolAddress) => Effect.Effect<ToolSchemaView | null, StorageFailure>; }; readonly providers: { readonly list: () => Effect.Effect<readonly ProviderKey[]>; readonly items: (key: ProviderKey) => Effect.Effect<readonly ProviderEntry[], StorageFailure>; }; readonly policies: { readonly list: () => Effect.Effect<readonly ToolPolicy[], StorageFailure>; readonly create: (input: CreateToolPolicyInput) => Effect.Effect<ToolPolicy, StorageFailure>; readonly update: (input: UpdateToolPolicyInput) => Effect.Effect<ToolPolicy, StorageFailure>; readonly remove: (input: RemoveToolPolicyInput) => Effect.Effect<void, StorageFailure>; readonly resolve: (address: ToolAddress) => Effect.Effect<EffectivePolicy, StorageFailure>; }; /** * The PLATFORM VIEW: read-only, tenant-wide reads across every subject. * Present only when the executor was built with `platformView: true` * (default off) — every other surface on this executor stays bound to the * single `{ tenant, subject }` product view and is unaffected by this one. * Internal admin surface; the public HTTP shape is a separate concern. */ readonly admin?: ExecutorAdmin; /** Saved generative-UI artifacts, visible to the bound owner scope. */ readonly artifacts: { /** Newest first, without the JSX source — lists stay light. */ readonly list: () => Effect.Effect<readonly ArtifactSummary[], StorageFailure>; readonly get: (id: string) => Effect.Effect<Artifact, ArtifactNotFoundError | StorageFailure>; /** Create, or overwrite an existing artifact in place when `id` is given. */ readonly save: ( input: SaveArtifactInput, ) => Effect.Effect<Artifact, ArtifactNotFoundError | StorageFailure>; readonly rename: ( input: RenameArtifactInput, ) => Effect.Effect<Artifact, ArtifactNotFoundError | StorageFailure>; readonly remove: (input: RemoveArtifactInput) => Effect.Effect<void, StorageFailure>; /** Upgrade the stored preview to a snapshot of a settled render. Touches * only `preview`, and never `updated_at`. */ readonly setPreview: ( input: SetArtifactPreviewInput, ) => Effect.Effect<void, ArtifactNotFoundError | StorageFailure>; }; /** * Approvals recorded for artifact-originated calls that paused on a human. * * Scoped to this executor's owner, so a record is only readable by the caller * who created it — the ownership check on resume is the same read that fetches * it. See `pending-approval.ts` for why an artifact pause is reconstructible * when a general codemode pause is not. */ readonly pendingApprovals: PendingApprovalStore; readonly execute: ( address: ToolAddress, args: unknown, options?: InvokeOptions, ) => Effect.Effect<unknown, ExecuteError>; readonly close: () => Effect.Effect<void, StorageFailure>;} & PluginExtensions<TPlugins>; // ---------------------------------------------------------------------------// The platform view — internal admin reads.//// Everything else on the executor is the PRODUCT view: bound to one// { tenant, subject }. This is the read-only escape hatch beside it, reading// the same store through a `reach: "tenant"` context so it can answer// "who exists under this tenant, and what have they connected".//// Vocabulary note: internal code says subject/tenant/reach. Anything// public-facing says users/owners — the translation happens at the HTTP edge,// not here.//// FIELD DISCIPLINE: these shapes are a hand-picked allowlist, not a projection// of the row. Nothing secret-bearing may appear — no `item_ids`, no// `refresh_item_id`, no oauth client secrets, no credential material of any// kind. Adding a field here is a deliberate act.// --------------------------------------------------------------------------- /** One principal seen under the tenant (a row of the `subject` table). */export interface AdminSubject { /** The host-auth principal id. Opaque — it also carries host sentinels * like "local", so nothing may parse it. */ readonly externalId: string; readonly createdAt: Date; /** Epoch ms of the last sighting on the request path; null when never seen * (a subject can be created at connection-create before any sighting). */ readonly lastSeenAt: number | null; readonly status: string | null;} /** A connection as the platform view sees it: enough to answer "what has this * user connected and is it healthy", and nothing that could resolve a * credential. */export interface AdminConnection { readonly owner: Owner; /** The owning principal — `null` for org-owned connections, which belong to * the tenant rather than to any one user. */ readonly subject: string | null; readonly integration: IntegrationSlug; readonly name: ConnectionName; /** The scope set the provider actually granted, space-delimited as recorded * at connect/refresh. Null for static credentials. A summary of ACCESS — * never a token. */ readonly oauthScope: string | null; readonly lastHealth: HealthCheckResult | null;} /** A subject together with every connection it owns in the tenant. */export interface AdminSubjectWithConnections extends AdminSubject { readonly connections: readonly AdminConnection[];} export interface AdminListSubjectsOptions { readonly limit?: number; readonly offset?: number;} /** * Page size applied when a caller names none. Every admin list is BOUNDED: * `listSubjects()` with no arguments is the obvious call, and unbounded it * returns every subject in the tenant — which `listSubjectsWithConnections` * then turns into one sequential connection query PER SUBJECT, inside a single * request, over a per-request socket on cloud. A default is what keeps the * no-args call honest; a caller who wants more asks for more, up to * {@link ADMIN_MAX_PAGE_SIZE}. *Lines 1–500 of 4853. Load more
import { Effect, Inspectable, Layer, Option, Predicate, Schema } from "effect"; import { FetchHttpClient, type HttpClient } from "effect/unstable/http"; import { fumadb } from "@executor-js/fumadb"; import { memoryAdapter } from "@executor-js/fumadb/adapters/memory"; import { withQueryContext, type Condition, type ConditionBuilder } from "@executor-js/fumadb/query"; import { schema as fumaSchema, type RelationsMap } from "@executor-js/fumadb/schema"; import type { AnyColumn } from "@executor-js/fumadb/schema"; import { StorageError, afterCommit, isStorageFailure, makeFumaClient, type FumaDb, type FumaRow, type FumaTables, type StorageFailure, } from "./fuma-runtime"; import { makeFumaBlobStore, pluginBlobStore, type BlobStore, type OwnerPartitions } from "./blob"; import { makePendingApprovalStore, type PendingApprovalStore } from "./pending-approval"; import { coreToolsPlugin } from "./core-tools"; import type { Connection, ConnectionInputOrigin, ConnectionRef, CreateConnectionInput, ConnectionValueInput, UpdateConnectionInput, ValidateConnectionInput, } from "./connection"; import { HealthCheckResult, HealthCheckSpec } from "./health-check"; import type { HealthCheckCandidate } from "./health-check"; import { ARTIFACT_SUMMARY_COLUMNS, coreSchema, isToolPolicyAction, TOOL_INVOCATION_COLUMNS, type ConnectionRow, type CoreSchema, type IntegrationRow, type OAuthClientRow, type ToolInvocationRow, type ToolRow, type ToolPolicyRow, } from "./core-schema"; import { ElicitationDeclinedError, ElicitationResponse, FormElicitation, type ElicitationHandler, type ElicitationRequest, type OnElicitation, type InvokeOptions, } from "./elicitation"; export type { OnElicitation, InvokeOptions } from "./elicitation"; import { rowToArtifact, rowToArtifactSummary, type Artifact, type ArtifactSummary, type RemoveArtifactInput, type RenameArtifactInput, type SaveArtifactInput, type SetArtifactPreviewInput, } from "./artifact"; import { ArtifactNotFoundError, ConnectionNotFoundError, CredentialProviderNotRegisteredError, CredentialResolutionError, IntegrationNotFoundError, InvalidConnectionInputError, IntegrationRemovalNotAllowedError, NoHandlerError, PluginNotLoadedError, ToolBlockedError, ToolInvocationError, ToolNotFoundError, type ExecuteError, } from "./errors"; import { ArtifactId, AuthTemplateSlug, ConnectionAddress, ConnectionName, IntegrationSlug, NO_AUTH_TEMPLATE, OAuthClientSlug, Owner, PolicyId, ProviderItemId, ProviderKey, Subject, Tenant, ToolAddress, ToolName, } from "./ids"; import type { AuthMethodDescriptor, Integration, IntegrationChangeEvent, IntegrationConfig, IntegrationDisplayDescriptor, RegisterIntegrationInput, } from "./integration"; import { makeOAuthService, type MintOAuthConnectionInput, type OAuthScopePolicy, } from "./oauth-service"; import type { OAuthService } from "./oauth-client"; import { comparePolicyRow, isValidPattern, matchPattern, positionForNewPattern, resolveEffectivePolicy, rowToToolPolicy, type CreateToolPolicyInput, type EffectivePolicy, type RemoveToolPolicyInput, type ToolPolicy, type UpdateToolPolicyInput, } from "./policies"; import type { CredentialProvider, ProviderEntry } from "./provider"; import { touchSubject } from "./subject-registry"; import type { AnyPlugin, Elicit, IntegrationConfigureSchema, IntegrationPresetCatalogEntry, IntegrationRecord, OwnerBinding, PluginCtx, PluginExtensions, ResolveToolsResult, StaticIntegrationDecl, StaticToolDecl, StorageDeps, ToolPolicyProvider, ToolPolicyProviderRule, ToolInvocationCredential, } from "./plugin"; import { pluginStorageId, type PluginStorageCollectionData, type PluginStorageCollectionDefinition, type PluginStorageCollectionQueryInput, type PluginStorageEntry, type PluginStorageFacade, type PluginStorageRuntimeCollectionDefinition, type PluginStorageRuntimeIndexSpec, } from "./plugin-storage"; import { assertExecutorOwnerPolicyTable, ORG_SUBJECT, type ExecutorOwnerPolicyContext, } from "./owner-policy"; import { ToolSchemaView, type IntegrationDetectionResult } from "./types"; import { type Tool, type ToolAnnotations, type ToolDef, type ToolListFilter } from "./tool"; import { buildToolTypeScriptPreview } from "./schema-types"; import { collectReferencedDefinitions } from "./schema-refs"; import { refreshAccessToken, exchangeClientCredentials, shouldRefreshToken, type OAuthEndpointUrlPolicy, } from "./oauth-helpers"; import { connectionIdentifier } from "./connection-name-identifier"; import { annotateToolResultOutcome } from "./tool-result"; import { isUnauthorizedToolFailure } from "./auth-tool-failure"; const PLUGIN_STORAGE_DELETE_KEY_BATCH_SIZE = 90; const PLUGIN_STORAGE_CREATE_ROW_BATCH_SIZE = 90; const MAX_APPROVAL_ARGUMENT_PREVIEW_CHARS = 4_000; // --------------------------------------------------------------------------- // Elicitation handler — resolved once at `createExecutor({ onElicitation })` // and overridable per `execute`. A tool that requests user input mid-execution // suspends the fiber and the handler decides how to respond. The "accept-all" // sentinel auto-accepts (tests / non-interactive hosts). // --------------------------------------------------------------------------- const acceptAllHandler: ElicitationHandler = () => Effect.succeed(ElicitationResponse.make({ action: "accept" })); const resolveElicitationHandler = (onElicitation: OnElicitation): ElicitationHandler => onElicitation === "accept-all" ? acceptAllHandler : onElicitation; // --------------------------------------------------------------------------- // Address scheme — `tools.<integration>.<owner>.<connection>.<tool>`. // --------------------------------------------------------------------------- const ADDRESS_PREFIX = "tools"; export interface ParsedToolAddress { readonly integration: IntegrationSlug; readonly owner: Owner; readonly connection: ConnectionName; readonly tool: ToolName; } const isOwner = (value: string): value is Owner => value === "org" || value === "user"; /** Parse a callable address; null when it's not a well-formed * `tools.<integration>.<owner>.<connection>.<tool>`. * * The four leading segments (prefix, integration, owner, connection) are * slug-like and never contain a `.`; the `<tool>` segment is the *entire* * remainder after the 4th dot, so it may itself contain dots. That lets a tool * whose name is a structured `group.leaf` path (e.g. an OpenAPI * `aliases.deleteAlias`) address naturally as * `tools.<integration>.<owner>.<connection>.aliases.deleteAlias` — the same * dotted nesting the sandbox `tools` proxy produces from property access. */ export const parseToolAddress = (address: string): ParsedToolAddress | null => { // Walk to the 4th dot; everything past it is the tool (dots and all). let cut = -1; for (let i = 0; i < 4; i++) { cut = address.indexOf(".", cut + 1); if (cut === -1) return null; } const [prefix, integration, owner, connection] = address.slice(0, cut).split(".") as [ string, string, string, string, ]; const tool = address.slice(cut + 1); if (prefix !== ADDRESS_PREFIX) return null; if (!isOwner(owner)) return null; if (integration.length === 0 || connection.length === 0 || tool.length === 0) { return null; } return { integration: IntegrationSlug.make(integration), owner, connection: ConnectionName.make(connection), tool: ToolName.make(tool), }; }; export const connectionAddress = ( owner: Owner, integration: IntegrationSlug, connection: ConnectionName, ): ConnectionAddress => ConnectionAddress.make(`${ADDRESS_PREFIX}.${integration}.${owner}.${connection}`); export const toolAddress = ( owner: Owner, integration: IntegrationSlug, connection: ConnectionName, tool: ToolName, ): ToolAddress => ToolAddress.make(`${ADDRESS_PREFIX}.${integration}.${owner}.${connection}.${tool}`); // --------------------------------------------------------------------------- // Owner key helpers — every owned-row write stamps `tenant`, `owner`, // `subject` (org → subject=""). // --------------------------------------------------------------------------- interface OwnedKeys { readonly tenant: string; readonly owner: Owner; readonly subject: string; } // --------------------------------------------------------------------------- // Executor — public surface. Every list/execute/schema call is a direct // core-table query unioned with the in-memory static pool. // --------------------------------------------------------------------------- export type Executor<TPlugins extends readonly AnyPlugin[] = readonly []> = { readonly integrations: { readonly list: () => Effect.Effect<readonly Integration[], StorageFailure>; readonly get: (slug: IntegrationSlug) => Effect.Effect<Integration | null, StorageFailure>; readonly update: ( slug: IntegrationSlug, patch: { readonly name?: string; readonly description?: string }, ) => Effect.Effect<void, IntegrationNotFoundError | StorageFailure>; readonly remove: ( slug: IntegrationSlug, ) => Effect.Effect<void, IntegrationRemovalNotAllowedError | StorageFailure>; readonly detect: ( url: string, ) => Effect.Effect<readonly IntegrationDetectionResult[], StorageFailure>; /** The integration's declared health check: which authenticated operation a * connection runs to prove its credential is alive and surface whose * account it is. Configured by the user the same way auth methods are. */ readonly healthCheck: { /** The currently declared check, or null when none is configured (or the * owning plugin has no health-check capability). */ readonly get: ( slug: IntegrationSlug, ) => Effect.Effect<HealthCheckSpec | null, StorageFailure>; /** The operations a user can pick from, ranked non-destructive-first then * fewest required arguments. Empty when the plugin has no candidates. */ readonly candidates: ( slug: IntegrationSlug, ) => Effect.Effect< readonly HealthCheckCandidate[], IntegrationNotFoundError | StorageFailure >; /** Declare (or clear, with `null`) the health check for the integration. */ readonly set: ( slug: IntegrationSlug, spec: HealthCheckSpec | null, ) => Effect.Effect<void, IntegrationNotFoundError | StorageFailure>; }; }; readonly connections: { readonly create: ( input: CreateConnectionInput, ) => Effect.Effect< Connection, | IntegrationNotFoundError | CredentialProviderNotRegisteredError | InvalidConnectionInputError | StorageFailure >; readonly list: (filter?: { readonly integration?: IntegrationSlug; readonly owner?: Owner; }) => Effect.Effect<readonly Connection[], StorageFailure>; readonly get: (ref: ConnectionRef) => Effect.Effect<Connection | null, StorageFailure>; /** Edit user-curated metadata (description, identityLabel). Credentials and * OAuth lifecycle fields are not editable here. */ readonly update: ( ref: ConnectionRef, input: UpdateConnectionInput, ) => Effect.Effect<Connection, ConnectionNotFoundError | StorageFailure>; readonly remove: ( ref: ConnectionRef, ) => Effect.Effect<void, ConnectionNotFoundError | StorageFailure>; readonly refresh: ( ref: ConnectionRef, ) => Effect.Effect< readonly Tool[], ConnectionNotFoundError | IntegrationNotFoundError | StorageFailure >; /** Run the integration's declared health check against a saved connection: * classify the credential (healthy / expired / degraded / unknown) and * extract its identity for display. Never throws on an auth wall or upstream * error: those come back as a `HealthCheckResult` with the matching status. */ readonly checkHealth: ( ref: ConnectionRef, options?: { /** Return the persisted verdict when younger than this; probe * otherwise. Omit to always probe. */ readonly ifStaleMs?: number; }, ) => Effect.Effect< HealthCheckResult, ConnectionNotFoundError | IntegrationNotFoundError | StorageFailure >; /** Validate an in-flight credential WITHOUT saving it (key-first connect): * resolve the pasted value(s), run the health check, and return the result * so the caller can confirm the key works and derive a name from the * identity before creating the connection. */ readonly validate: ( input: ValidateConnectionInput, ) => Effect.Effect<HealthCheckResult, IntegrationNotFoundError | StorageFailure>; }; /** Shared OAuth service. Hosts use this through the core HTTP OAuth group; * plugins see the same service as `ctx.oauth`. */ readonly oauth: OAuthService; readonly tools: { readonly list: (filter?: ToolListFilter) => Effect.Effect<readonly Tool[], StorageFailure>; readonly schema: (address: ToolAddress) => Effect.Effect<ToolSchemaView | null, StorageFailure>; }; readonly providers: { readonly list: () => Effect.Effect<readonly ProviderKey[]>; readonly items: (key: ProviderKey) => Effect.Effect<readonly ProviderEntry[], StorageFailure>; }; readonly policies: { readonly list: () => Effect.Effect<readonly ToolPolicy[], StorageFailure>; readonly create: (input: CreateToolPolicyInput) => Effect.Effect<ToolPolicy, StorageFailure>; readonly update: (input: UpdateToolPolicyInput) => Effect.Effect<ToolPolicy, StorageFailure>; readonly remove: (input: RemoveToolPolicyInput) => Effect.Effect<void, StorageFailure>; readonly resolve: (address: ToolAddress) => Effect.Effect<EffectivePolicy, StorageFailure>; }; /** * The PLATFORM VIEW: read-only, tenant-wide reads across every subject. * Present only when the executor was built with `platformView: true` * (default off) — every other surface on this executor stays bound to the * single `{ tenant, subject }` product view and is unaffected by this one. * Internal admin surface; the public HTTP shape is a separate concern. */ readonly admin?: ExecutorAdmin; /** Saved generative-UI artifacts, visible to the bound owner scope. */ readonly artifacts: { /** Newest first, without the JSX source — lists stay light. */ readonly list: () => Effect.Effect<readonly ArtifactSummary[], StorageFailure>; readonly get: (id: string) => Effect.Effect<Artifact, ArtifactNotFoundError | StorageFailure>; /** Create, or overwrite an existing artifact in place when `id` is given. */ readonly save: ( input: SaveArtifactInput, ) => Effect.Effect<Artifact, ArtifactNotFoundError | StorageFailure>; readonly rename: ( input: RenameArtifactInput, ) => Effect.Effect<Artifact, ArtifactNotFoundError | StorageFailure>; readonly remove: (input: RemoveArtifactInput) => Effect.Effect<void, StorageFailure>; /** Upgrade the stored preview to a snapshot of a settled render. Touches * only `preview`, and never `updated_at`. */ readonly setPreview: ( input: SetArtifactPreviewInput, ) => Effect.Effect<void, ArtifactNotFoundError | StorageFailure>; }; /** * Approvals recorded for artifact-originated calls that paused on a human. * * Scoped to this executor's owner, so a record is only readable by the caller * who created it — the ownership check on resume is the same read that fetches * it. See `pending-approval.ts` for why an artifact pause is reconstructible * when a general codemode pause is not. */ readonly pendingApprovals: PendingApprovalStore; readonly execute: ( address: ToolAddress, args: unknown, options?: InvokeOptions, ) => Effect.Effect<unknown, ExecuteError>; readonly close: () => Effect.Effect<void, StorageFailure>; } & PluginExtensions<TPlugins>; // --------------------------------------------------------------------------- // The platform view — internal admin reads. // // Everything else on the executor is the PRODUCT view: bound to one // { tenant, subject }. This is the read-only escape hatch beside it, reading // the same store through a `reach: "tenant"` context so it can answer // "who exists under this tenant, and what have they connected". // // Vocabulary note: internal code says subject/tenant/reach. Anything // public-facing says users/owners — the translation happens at the HTTP edge, // not here. // // FIELD DISCIPLINE: these shapes are a hand-picked allowlist, not a projection // of the row. Nothing secret-bearing may appear — no `item_ids`, no // `refresh_item_id`, no oauth client secrets, no credential material of any // kind. Adding a field here is a deliberate act. // --------------------------------------------------------------------------- /** One principal seen under the tenant (a row of the `subject` table). */ export interface AdminSubject { /** The host-auth principal id. Opaque — it also carries host sentinels * like "local", so nothing may parse it. */ readonly externalId: string; readonly createdAt: Date; /** Epoch ms of the last sighting on the request path; null when never seen * (a subject can be created at connection-create before any sighting). */ readonly lastSeenAt: number | null; readonly status: string | null; } /** A connection as the platform view sees it: enough to answer "what has this * user connected and is it healthy", and nothing that could resolve a * credential. */ export interface AdminConnection { readonly owner: Owner; /** The owning principal — `null` for org-owned connections, which belong to * the tenant rather than to any one user. */ readonly subject: string | null; readonly integration: IntegrationSlug; readonly name: ConnectionName; /** The scope set the provider actually granted, space-delimited as recorded * at connect/refresh. Null for static credentials. A summary of ACCESS — * never a token. */ readonly oauthScope: string | null; readonly lastHealth: HealthCheckResult | null; } /** A subject together with every connection it owns in the tenant. */ export interface AdminSubjectWithConnections extends AdminSubject { readonly connections: readonly AdminConnection[]; } export interface AdminListSubjectsOptions { readonly limit?: number; readonly offset?: number; } /** * Page size applied when a caller names none. Every admin list is BOUNDED: * `listSubjects()` with no arguments is the obvious call, and unbounded it * returns every subject in the tenant — which `listSubjectsWithConnections` * then turns into one sequential connection query PER SUBJECT, inside a single * request, over a per-request socket on cloud. A default is what keeps the * no-args call honest; a caller who wants more asks for more, up to * {@link ADMIN_MAX_PAGE_SIZE}. *