import { defineAction } from "@agent-native/core/action"; import { listWorkspaceConnectionProviders, type WorkspaceConnectionCapability, type WorkspaceConnectionTemplateUse, } from "@agent-native/core/connections"; import { isProviderApiId, listProviderApiCatalog, } from "@agent-native/core/provider-api"; import { CredentialStoreUnavailableError, hasWorkspaceProviderOAuthCredentials, isGoogleWorkspaceOAuthProvider, } from "@agent-native/core/server"; import { getWorkspaceConnectionAppAccess, listWorkspaceConnectionGrants, listWorkspaceConnectionsForUser, summarizeWorkspaceConnectionProviderReadiness, } from "@agent-native/core/workspace-connections"; import { dispatchActions } from "@agent-native/dispatch/actions"; import { z } from "zod"; const httpBoolean = z.preprocess((value) => { if (typeof value !== "string") return value; const normalized = value.trim().toLowerCase(); if (["true", "1", "yes", "on"].includes(normalized)) return true; if (["false", "0", "no", "off"].includes(normalized)) return false; return value; }, z.boolean()); type GrantApp = { id: string; label: string; }; type WorkspaceApp = { id: string; name?: string; status?: "ready" | "pending"; archived?: boolean; }; type GrantSummary = { id: string; connectionId: string; provider: string; appId: string; access: "all-apps" | "selected-app" | "explicit-grant"; lastUsedAt?: string | null; }; type GoogleOAuthAvailability = "configured" | "unconfigured" | "unavailable"; function unique(values: string[]) { return Array.from( new Set(values.map((value) => value.trim()).filter(Boolean)), ); } function optionalTimestamp(source: object, key: string) { if (!Object.prototype.hasOwnProperty.call(source, key)) return undefined; const value = (source as Record)[key]; if (value == null) return null; if (value instanceof Date) return value.toISOString(); return typeof value === "string" ? value : (JSON.stringify(value) ?? ""); } function humanizeAppId(appId: string): string { return appId .split(/[-_]/) .filter(Boolean) .map((part) => part.charAt(0).toUpperCase() + part.slice(1)) .join(" "); } async function listGrantApps(): Promise { const listWorkspaceApps = dispatchActions["list-workspace-apps"]; if (!listWorkspaceApps) return [{ id: "dispatch", label: "Dispatch" }]; try { const apps = (await listWorkspaceApps.run({ includeAgentCards: false, audience: "all", } as any)) as WorkspaceApp[]; const grantApps = apps .filter((app) => !app.archived && app.status !== "pending") .map((app) => ({ id: app.id, label: app.name || humanizeAppId(app.id), })); return grantApps.length > 0 ? grantApps : [{ id: "dispatch", label: "Dispatch" }]; } catch { return [{ id: "dispatch", label: "Dispatch" }]; } } export default defineAction({ description: "List the workspace integration provider catalog, saved shared connections, and app access grants.", schema: z.object({ provider: z .string() .optional() .describe("Optional provider ID such as slack, github, or notion."), appId: z .string() .optional() .describe("Only include connections available to this app ID."), includeDisabled: httpBoolean .default(false) .describe("Include disabled connections. Defaults to false."), capability: z .string() .optional() .describe("Optional capability filter such as search, import, or docs."), templateUse: z .string() .optional() .describe("Optional template-use filter such as brain or analytics."), }), http: { method: "GET" }, run: async (args) => { const catalogProviders = listWorkspaceConnectionProviders({ capability: args.capability as WorkspaceConnectionCapability | undefined, templateUse: args.templateUse as | WorkspaceConnectionTemplateUse | undefined, }); let googleOAuthAvailability: GoogleOAuthAvailability; try { googleOAuthAvailability = (await hasWorkspaceProviderOAuthCredentials( "gmail", )) ? "configured" : "unconfigured"; } catch (error) { if (!(error instanceof CredentialStoreUnavailableError)) throw error; googleOAuthAvailability = "unavailable"; } const googleOAuthConfigured = googleOAuthAvailability === "configured"; const providers = catalogProviders.filter( (provider) => (!args.provider || provider.id === args.provider) && (googleOAuthConfigured || !isGoogleWorkspaceOAuthProvider(provider.id)), ); const allConnections = await listWorkspaceConnectionsForUser({ provider: args.provider, appId: args.appId, includeDisabled: args.includeDisabled, }); const connections = allConnections.filter( (connection) => googleOAuthConfigured || !isGoogleWorkspaceOAuthProvider(connection.provider), ); const allExplicitGrants = await listWorkspaceConnectionGrants({ provider: args.provider, appId: args.appId, }); const visibleConnectionIds = new Set( connections.map((connection) => connection.id), ); const explicitGrants = allExplicitGrants.filter( (grant) => visibleConnectionIds.has(grant.connectionId) && (googleOAuthConfigured || !isGoogleWorkspaceOAuthProvider(grant.provider)), ); const grantApps = await listGrantApps(); const legacyGrants = connections.flatMap((connection) => { if (connection.allowedApps.length === 0) { return [ { id: `${connection.id}:all-apps`, connectionId: connection.id, provider: connection.provider, appId: "*", access: "all-apps" as const, }, ]; } return connection.allowedApps.map( (appId): GrantSummary => ({ id: `${connection.id}:${appId}`, connectionId: connection.id, provider: connection.provider, appId, access: "selected-app" as const, }), ); }); const grants: GrantSummary[] = [ ...legacyGrants, ...explicitGrants.map((grant) => { const lastUsedAt = optionalTimestamp(grant, "lastUsedAt"); return { id: grant.id, connectionId: grant.connectionId, provider: grant.provider, appId: grant.appId, access: "explicit-grant" as const, ...(lastUsedAt !== undefined ? { lastUsedAt } : {}), }; }), ]; const grantSummaries = connections.map((connection) => { const explicitGrantAppIds = unique( explicitGrants .filter((grant) => grant.connectionId === connection.id) .map((grant) => grant.appId), ); const selectedAppIds = unique(connection.allowedApps); const allApps = selectedAppIds.length === 0; const effectiveAppIds = allApps ? ["*"] : unique([...selectedAppIds, ...explicitGrantAppIds]); return { connectionId: connection.id, provider: connection.provider, accessMode: allApps ? ("all-apps" as const) : ("selected-apps" as const), allApps, selectedAppIds, explicitGrantAppIds, effectiveAppIds, trackedApps: grantApps.map((app) => { const access = getWorkspaceConnectionAppAccess( connection, app.id, explicitGrants, ); return { appId: app.id, label: app.label, granted: access.available, mode: access.mode, grantId: access.grantId, }; }), }; }); const providersWithReadiness = providers.map((provider) => { const providerApi = isProviderApiId(provider.id) ? listProviderApiCatalog(provider.id)[0] : null; return { ...provider, readiness: summarizeWorkspaceConnectionProviderReadiness({ provider, connections, grants: explicitGrants, appId: args.appId, includeConnections: "all", }), rawProviderApi: providerApi ? { available: true, actionNames: [ "provider-api-catalog", "provider-api-docs", "provider-api-request", ], docsUrls: providerApi.docsUrls, specUrls: providerApi.specUrls, auth: providerApi.auth, examples: providerApi.examples, } : { available: false, actionNames: [], docsUrls: [], specUrls: [], auth: null, examples: [], }, }; }); return { availability: { googleOAuth: { status: googleOAuthAvailability, retryable: googleOAuthAvailability === "unavailable", }, }, providers: providersWithReadiness, connections, grants, grantSummaries, suggestedApps: grantApps, counts: { providers: providersWithReadiness.length, connections: connections.length, grants: grants.length, allAppConnections: grantSummaries.filter((summary) => summary.allApps) .length, selectedAppConnections: grantSummaries.filter( (summary) => !summary.allApps, ).length, readyProviders: providersWithReadiness.filter( (provider) => provider.readiness.status === "ready", ).length, }, }; }, });