History

Rename the integration entity from 'sources' to 'integrations' (#1370)

Completes the v2 naming fold: config key, SDK types, plugin contract fields, sandbox tool paths, CLI subcommand, UI copy, docs, and e2e selectors now say integration. v1 migration readers keep the old names they read from real data; the v1 sourceUrl field maps to specUrl at the migration boundary.

Rhys Sullivan committed 4 months ago

Commit 77c7cbc1 parent 55a2463151 files · +659 −627Browse files

Unified · Split

151 files
  1. AGENTS.md +1 −1
  2. README.md +7 −7
  3. apps/cli/src/main.ts +11 −7
  4. apps/cli/src/tooling.ts +2 −2
  5. apps/cloud/src/db/db.schema.test.ts +2 −2
  6. apps/cloud/src/db/db.ts +1 −1
  7. apps/cloud/src/mcp-session.e2e.node.test.ts +1 −1
  8. apps/cloud/src/web/pages/login.tsx +3 −1
  9. apps/cloud/vite.config.ts +1 −1
  10. apps/docs/concepts/integrations.mdx +1 −1
  11. apps/docs/local/cli.mdx +3 −3
  12. apps/host-selfhost/src/integrations-mcp.test.ts +2 −2
  13. apps/host-selfhost/src/integrations.test.ts +0 −0
  14. apps/local/src/auth-tool-failures.test.ts +2 −2
  15. apps/local/src/mcp-browser-resume.test.ts +1 −1
  16. apps/local/src/serve.test.ts +1 −1
  17. apps/marketing/src/pages/privacy.astro +2 −2
  18. design.md +6 −6
  19. e2e/cli/service-lifecycle.test.ts +2 −2
  20. e2e/cloud/auth-routing-flow.test.ts +1 −1
  21. e2e/cloud/cli-device-login.test.ts +3 −3
  22. e2e/cloud/integrations-api.test.ts +0 −0
  23. e2e/cloud/integrations-refresh.test.ts +0 −0
  24. e2e/cloud/oauth-callback-unauthenticated.test.ts +1 −1
  25. e2e/cloud/toolkit-opencode-real.test.ts +3 −3
  26. e2e/cloud/toolkit-policy-perf.test.ts +8 −8
  27. e2e/cloud/unauthenticated-skeleton.test.ts +4 −2
  28. e2e/local/auth.test.ts +1 −1
  29. e2e/local/update-notice.test.ts +1 −1
  30. e2e/scenarios/auth-methods.test.ts +4 −4
  31. e2e/scenarios/microsoft-emulator.test.ts +1 −1
  32. e2e/scenarios/microsoft-graph-default.test.ts +3 −1
  33. e2e/scenarios/microsoft-graph-full.test.ts +3 −1
  34. e2e/scenarios/openapi-add-integration-action-bar.test.ts +4 −4
  35. e2e/scenarios/openapi-integration.test.ts +0 −0
  36. e2e/scenarios/openapi-server-selection-ui.test.ts +2 −2
  37. e2e/scenarios/support/large-catalog.ts +29 −29
  38. e2e/scenarios/tool-descriptions.test.ts +2 −2
  39. e2e/selfhost/cli-device-login.test.ts +3 −3
  40. packages/app/src/web/shell.tsx +5 −5
  41. packages/app/vite.ts +1 −1
  42. packages/core/api/src/plugin-routes.ts +1 −1
  43. packages/core/config/src/config.test.ts +37 −35
  44. packages/core/config/src/index.ts +7 −7
  45. packages/core/config/src/schema.ts +16 −16
  46. packages/core/config/src/sink.ts +10 −10
  47. packages/core/config/src/write.ts +24 −24
  48. packages/core/execution/src/engine.test.ts +1 −1
  49. packages/core/execution/src/engine.ts +7 −7
  50. packages/core/execution/src/index.ts +1 −1
  51. packages/core/execution/src/tool-invoker.leak.test.ts +1 −1
  52. packages/core/execution/src/tool-invoker.test.ts +11 −11
  53. packages/core/execution/src/tool-invoker.ts +17 −17
  54. packages/core/sdk/README.md +3 −3
  55. packages/core/sdk/src/auth-tool-failure.ts +7 −5
  56. packages/core/sdk/src/blob.ts +1 −1
  57. packages/core/sdk/src/client.test.ts +2 −2
  58. packages/core/sdk/src/client.ts +5 −5
  59. packages/core/sdk/src/core-tools.ts +1 −1
  60. packages/core/sdk/src/executor.ts +38 −36
  61. packages/core/sdk/src/index.ts +1 −1
  62. packages/core/sdk/src/migration-spec.test.ts +2 −2
  63. packages/core/sdk/src/migration-spec.ts +2 −2
  64. packages/core/sdk/src/plugin.ts +13 −11
  65. packages/core/sdk/src/policies.test.ts +1 −1
  66. packages/core/sdk/src/promise-executor.ts +1 −1
  67. packages/core/sdk/src/promise.test.ts +2 −2
  68. packages/core/sdk/src/sqlite-config-blob-migration.test.ts +2 −2
  69. packages/hosts/mcp/src/tool-server.test.ts +1 −1
  70. packages/kernel/core/src/strip-types.test.ts +1 −1
  71. packages/kernel/ir/src/registry.ts +2 −2
  72. packages/kernel/ir/src/serialize.ts +2 −2
  73. packages/plugins/desktop-settings/src/client.tsx +5 −5
  74. packages/plugins/desktop-settings/src/server.ts +1 −1
  75. packages/plugins/graphql/README.md +2 −2
  76. packages/plugins/graphql/src/react/AddGraphqlIntegration.tsx +6 −6
  77. packages/plugins/graphql/src/react/GraphqlAccountsPanel.tsx +3 −3
  78. packages/plugins/graphql/src/react/GraphqlIntegrationFields.tsx +1 −1
  79. packages/plugins/graphql/src/react/index.ts +1 −1
  80. packages/plugins/graphql/src/react/integration-plugin.ts +1 −1
  81. packages/plugins/graphql/src/react/plugin-client.tsx +1 −1
  82. packages/plugins/graphql/src/sdk/plugin.ts +4 −4
  83. packages/plugins/mcp/README.md +4 −4
  84. packages/plugins/mcp/src/react/AddMcpIntegration.tsx +3 −3
  85. packages/plugins/mcp/src/react/EditMcpIntegration.tsx +8 −5
  86. packages/plugins/mcp/src/react/McpAccountsPanel.tsx +3 −3
  87. packages/plugins/mcp/src/react/McpRemoteIntegrationFields.tsx +3 −3
  88. packages/plugins/mcp/src/react/McpSignInButton.tsx +2 −2
  89. packages/plugins/mcp/src/react/index.ts +2 −2
  90. packages/plugins/mcp/src/react/integration-plugin.tsx +5 −5
  91. packages/plugins/mcp/src/react/plugin-client.tsx +2 −2
  92. packages/plugins/mcp/src/sdk/plugin.test.ts +1 −1
  93. packages/plugins/mcp/src/sdk/plugin.ts +5 −5
  94. packages/plugins/mcp/src/sdk/probe-shape-real-servers.live.test.ts +1 −1
  95. packages/plugins/onepassword/src/sdk/plugin.ts +1 −1
  96. packages/plugins/openapi/README.md +1 −1
  97. packages/plugins/openapi/src/api/group.ts +1 −1
  98. packages/plugins/openapi/src/api/handlers.ts +1 −1
  99. packages/plugins/openapi/src/providers/google/config.ts +1 −1
  100. packages/plugins/openapi/src/providers/google/discovery.test.ts +1 −1
  101. packages/plugins/openapi/src/providers/google/openapi-ownership-migration.test.ts +1 −1
  102. packages/plugins/openapi/src/providers/google/presets.ts +1 −1
  103. packages/plugins/openapi/src/providers/google/spec-format-adapter.ts +1 −1
  104. packages/plugins/openapi/src/providers/microsoft/graph.ts +2 −2
  105. packages/plugins/openapi/src/providers/microsoft/presets.ts +1 −1
  106. packages/plugins/openapi/src/providers/microsoft/spec-format-adapter.ts +1 −1
  107. packages/plugins/openapi/src/react/AddOpenApiIntegration.test.ts +1 −1
  108. packages/plugins/openapi/src/react/AddOpenApiIntegration.tsx +16 −10
  109. packages/plugins/openapi/src/react/OpenApiAccountsPanel.tsx +3 −3
  110. packages/plugins/openapi/src/react/OpenApiIntegrationDetailsFields.tsx +1 −1
  111. packages/plugins/openapi/src/react/UpdateSpecSection.tsx +4 −4
  112. packages/plugins/openapi/src/react/index.ts +2 −2
  113. packages/plugins/openapi/src/react/integration-plugin.ts +1 −1
  114. packages/plugins/openapi/src/react/plugin-client.tsx +2 −2
  115. packages/plugins/openapi/src/sdk/backing.ts +1 −1
  116. packages/plugins/openapi/src/sdk/config.ts +1 −1
  117. packages/plugins/openapi/src/sdk/configure.test.ts +2 −2
  118. packages/plugins/openapi/src/sdk/describe-auth-methods.test.ts +3 −3
  119. packages/plugins/openapi/src/sdk/invoke.ts +6 −6
  120. packages/plugins/openapi/src/sdk/plugin.test.ts +2 −2
  121. packages/plugins/openapi/src/sdk/plugin.ts +26 −26
  122. packages/plugins/openapi/src/sdk/preview.ts +2 −2
  123. packages/plugins/openapi/src/sdk/real-specs.test.ts +1 −1
  124. packages/plugins/openapi/src/sdk/response-headers-timeout.test.ts +2 −2
  125. packages/plugins/openapi/src/sdk/spec-blob.test.ts +2 −2
  126. packages/plugins/openapi/src/sdk/spec-format.ts +1 −1
  127. packages/plugins/openapi/src/sdk/streaming-response.test.ts +2 −2
  128. packages/plugins/openapi/src/sdk/upstream-failures.test.ts +2 −2
  129. packages/plugins/openapi/src/testing/index.ts +20 −20
  130. packages/plugins/provider-service-split/src/planner.test.ts +5 −5
  131. packages/plugins/provider-service-split/src/planner.ts +4 −4
  132. packages/plugins/provider-service-split/src/sqlite.test.ts +1 −1
  133. packages/plugins/toolkits/src/page.tsx +11 −11
  134. packages/react/src/api/error-reporting.test.ts +6 −6
  135. packages/react/src/components/accounts-section.tsx +1 −1
  136. packages/react/src/components/command-palette.tsx +7 −7
  137. packages/react/src/components/integration-favicon.test.tsx +12 −10
  138. packages/react/src/components/integration-favicon.tsx +29 −29
  139. packages/react/src/components/integration-icon-with-account.tsx +2 −2
  140. packages/react/src/components/metadata-edit-sheet.tsx +3 −2
  141. packages/react/src/lib/integration-add.tsx +1 −1
  142. packages/react/src/multiplayer/shell.tsx +1 −1
  143. packages/react/src/pages/integration-detail.tsx +1 −1
  144. packages/react/src/pages/integrations.tsx +2 −2
  145. packages/react/src/plugins/integration-identity.tsx +7 −7
  146. packages/react/src/plugins/oauth-reconnect.test.ts +1 −1
  147. packages/react/src/plugins/oauth-reconnect.ts +1 −1
  148. scripts/agent-config-smoke.ts +5 −5
  149. tests/presets-reachable.test.ts +1 −1
  150. tests/tools-cli.test.ts +5 −5
  151. vision.md +25 −25
AGENTS.md+1−1
@@ -65,7 +65,7 @@ a given Git URL into `.reference` and pull latest before using it. · 64 unmodified lines
6565
6666 ## Package Roles
6767
68−- `packages/core/sdk`: core contracts, plugin wiring, scopes, sources, secrets,
68+- `packages/core/sdk`: core contracts, plugin wiring, scopes, integrations, secrets,
6969 policies, fixtures. `@executor-js/sdk/http-auth` carries the shared auth-method
7070 vocabulary the HTTP protocol plugins compose (core never imports it, keeping it
7171 carrier-agnostic).
README.md+7−7
@@ -21,9 +21,9 @@ set its policy once, and every agent shares it over MCP. Your integrations, · 20 unmodified lines
2121 auth, and policies live in one place instead of being scattered across each
2222 client.
2323
24−- **Any source.** First-party support for MCP servers, OpenAPI, GraphQL, and
24+- **Any integration.** First-party support for MCP servers, OpenAPI, GraphQL, and
2525 Google Discovery. If you can describe it with a JSON schema, it can be an
26− integration. The plugin system is open to any source type.
26+ integration. The plugin system is open to any integration type.
2727 - **One catalog, every agent.** Anything MCP-compatible connects to the same
2828 set of tools.
2929 - **Governed by policy.** Each tool is allowed, gated behind approval, or
@@ -100,12 +100,12 @@ appear. · 70 unmodified lines
100100
101101 ## Add an integration
102102
103−From the web UI, click **Add Source**, paste an OpenAPI, GraphQL, or MCP URL,
103+From the web UI, click **Add Integration**, paste an OpenAPI, GraphQL, or MCP URL,
104104 and Executor detects the type, indexes the tools, and handles auth. Or add one
105105 from the CLI:
106106
107107 ```bash
108−executor call executor openapi addSource '{
108+executor call executor openapi addIntegration '{
109109 "spec": "https://petstore3.swagger.io/api/v3/openapi.json",
110110 "namespace": "petstore",
111111 "baseUrl": "https://petstore3.swagger.io/api/v3"
@@ -113,7 +113,7 @@ executor call executor openapi addSource '{ · 1 unmodified line
113113 ```
114114
115115 Use `baseUrl` when the OpenAPI document has relative `servers` entries (for
116−example `"/api/v3"`). Confirm it is live with `executor tools sources`.
116+example `"/api/v3"`). Confirm it is live with `executor tools integrations`.
117117
118118 ## Using tools
119119
@@ -169,7 +169,7 @@ executor call <path...> --help # browse namespaces/resources/methods · 49 unmodified lines
169169 executor call <path...> --help --match "<text>" --limit <n> # narrow huge namespaces
170170 executor resume --execution-id <id> # resume paused execution
171171 executor tools search "<query>" # search tools by intent
172−executor tools sources # list configured sources + tool counts
172+executor tools integrations # list configured integrations + tool counts
173173 executor tools describe <path> # show tool TypeScript/JSON schema
174174 ```
175175
@@ -190,7 +190,7 @@ apps/ · 14 unmodified lines
190190 packages/
191191 core/ contracts, plugin wiring, scopes, policies, SDK, API, CLI core
192192 kernel/ execution runtimes (QuickJS, Deno subprocess, dynamic worker)
193− plugins/ source and provider plugins (openapi, graphql, mcp, google,
193+ plugins/ integration and provider plugins (openapi, graphql, mcp, google,
194194 microsoft, 1password, keychain, encrypted/file secrets, ...)
195195 hosts/ host adapters (MCP surface, Cloudflare)
196196 react/ shared React UI
apps/cli/src/main.ts+11−7
@@ -162,7 +162,7 @@ import { · 161 unmodified lines
162162 buildDescribeToolCode,
163163 filterToolPathChildren,
164164 buildInvokeToolCode,
165− buildListSourcesCode,
165+ buildListIntegrationsCode,
166166 buildSearchToolsCode,
167167 extractExecutionId,
168168 extractPausedInteraction,
@@ -2052,8 +2052,8 @@ const toolsSearchCommand = Command.make( · 1883 unmodified lines
20522052 }),
20532053 ).pipe(Command.withDescription("Search tools by natural-language query"));
20542054
2055−const toolsSourcesCommand = Command.make(
2056− "sources",
2055+const toolsIntegrationsCommand = Command.make(
2056+ "integrations",
20572057 {
20582058 query: Options.string("query").pipe(Options.optional),
20592059 limit: Options.integer("limit").pipe(Options.withDefault(50)),
@@ -2065,7 +2065,7 @@ const toolsSourcesCommand = Command.make( · 5 unmodified lines
20652065 Effect.gen(function* () {
20662066 applyScope(scope);
20672067 const target = serverTargetFromOptions({ baseUrl, server });
2068− const code = buildListSourcesCode({
2068+ const code = buildListIntegrationsCode({
20692069 query: Option.getOrUndefined(query),
20702070 limit,
20712071 });
@@ -2077,7 +2077,7 @@ const toolsSourcesCommand = Command.make( · 5 unmodified lines
20772077 outcome: result.outcome,
20782078 });
20792079 }),
2080−).pipe(Command.withDescription("List configured sources and tool counts"));
2080+).pipe(Command.withDescription("List configured integrations and tool counts"));
20812081
20822082 const toolsDescribeCommand = Command.make(
20832083 "describe",
@@ -2102,8 +2102,12 @@ const toolsDescribeCommand = Command.make( · 18 unmodified lines
21022102 ).pipe(Command.withDescription("Describe a tool's TypeScript and JSON schema"));
21032103
21042104 const toolsCommand = Command.make("tools").pipe(
2105− Command.withSubcommands([toolsSearchCommand, toolsSourcesCommand, toolsDescribeCommand] as const),
2106− Command.withDescription("Discover available tools and sources"),
2105+ Command.withSubcommands([
2106+ toolsSearchCommand,
2107+ toolsIntegrationsCommand,
2108+ toolsDescribeCommand,
2109+ ] as const),
2110+ Command.withDescription("Discover available tools and integrations"),
21072111 );
21082112
21092113 const profileConnectionInput = (input: {
apps/cli/src/tooling.ts+2−2
@@ -408,14 +408,14 @@ export const buildSearchToolsCode = (input: { · 407 unmodified lines
408408 return `return await tools.search(${JSON.stringify(payload)});`;
409409 };
410410
411−export const buildListSourcesCode = (input: { query?: string; limit: number }): string => {
411+export const buildListIntegrationsCode = (input: { query?: string; limit: number }): string => {
412412 const payload: Record<string, unknown> = {
413413 limit: input.limit,
414414 };
415415 if (input.query && input.query.trim().length > 0) {
416416 payload.query = input.query;
417417 }
418− return `return await tools.executor.sources.list(${JSON.stringify(payload)});`;
418+ return `return await tools.executor.integrations.list(${JSON.stringify(payload)});`;
419419 };
420420
421421 export const buildDescribeToolCode = (toolPath: string): string =>
apps/cloud/src/db/db.schema.test.ts+2−2
@@ -2,7 +2,7 @@ · 1 unmodified line
22 // merge of cloud + executor schemas. If ANY call site builds its own
33 // `{ schema }` without spreading both, `db._.fullSchema` comes back
44 // missing tables and the drizzle adapter throws
5−// `unknown model "source"` at the first request that touches scoped data.
5+// `unknown model "integration"` at the first request that touches scoped data.
66 //
77 // We hit this in prod (MCP endpoint) when `mcp-session.ts` built
88 // `combinedSchema = { ...cloudSchema }` and forgot to spread the executor
@@ -56,7 +56,7 @@ describe("combinedSchema", () => { · 47 unmodified lines
5656 }
5757 });
5858
59− // Executor-schema drives the scope-sharded tables (source/tool/etc). If
59+ // Executor-schema drives the scope-sharded tables (integration/tool/etc). If
6060 // any of these go missing the drizzle adapter's `getTable` lookup throws.
6161 it("includes scope-sharded executor tables", () => {
6262 for (const key of Object.keys(executorSchema)) {
apps/cloud/src/db/db.ts+1−1
@@ -24,7 +24,7 @@ import * as executorSchema from "./executor-schema"; · 23 unmodified lines
2424
2525 // Exported so every drizzle() call in the cloud app shares one schema
2626 // object. Historically `mcp-session.ts` built its own and forgot to spread
27−// `executorSchema`, producing runtime "unknown model source" errors that
27+// `executorSchema`, producing runtime "unknown model integration" errors that
2828 // only surfaced in prod. See apps/cloud/src/db/db.schema.test.ts.
2929 export const combinedSchema = { ...cloudSchema, ...executorSchema };
3030
apps/cloud/src/mcp-session.e2e.node.test.ts+1−1
@@ -52,7 +52,7 @@ const EMPTY_INPUT_SCHEMA = Schema.toStandardSchemaV1( · 51 unmodified lines
5252 const elicitingTestPlugin = definePlugin(() => ({
5353 id: "eliciting-test" as const,
5454 storage: () => ({}),
55− staticSources: () => [
55+ staticIntegrations: () => [
5656 {
5757 id: "e2e",
5858 kind: "in-memory",
apps/cloud/src/web/pages/login.tsx+3−1
@@ -13,7 +13,9 @@ export const LoginPage = ({ returnTo }: { returnTo?: string | undefined }) => { · 12 unmodified lines
1313 <div className="mx-auto flex w-full max-w-sm flex-col items-center gap-6">
1414 <div className="flex flex-col items-center gap-2">
1515 <h1 className="font-mono text-4xl">Executor</h1>
16− <p className="text-sm text-muted-foreground">Sign in to manage your tools and sources</p>
16+ <p className="text-sm text-muted-foreground">
17+ Sign in to manage your tools and integrations
18+ </p>
1719 </div>
1820 <a
1921 href={loginHref}
apps/cloud/vite.config.ts+1−1
@@ -66,7 +66,7 @@ export default defineConfig(({ command, mode }) => { · 65 unmodified lines
6666 }
6767
6868 // Deps vite only discovers once a lazy-loaded React chunk actually renders
69− // (e.g. opening the MCP/OpenAPI "add source" flow). Discovering them mid-run
69+ // (e.g. opening the MCP/OpenAPI "add integration" flow). Discovering them mid-run
7070 // forces a re-optimize + full program reload; in workerd (apps/cloud's SSR
7171 // worker) each reload stacks a new isolate's heap on top of the last one
7272 // without freeing it, so a handful of reloads exhausts the worker's heap
apps/docs/concepts/integrations.mdx+1−1
@@ -4,7 +4,7 @@ description: "An integration is something Executor connects to, defined by an MC · 3 unmodified lines
44 ---
55
66 An **integration** is something Executor can connect to. Each integration is defined by
7−a source that describes the available tools:
7+a spec that describes the available tools:
88
99 - an **MCP server**
1010 - an **OpenAPI** specification
apps/docs/local/cli.mdx+3−3
@@ -74,12 +74,12 @@ the CLI: · 73 unmodified lines
7474
7575 ## Add an integration
7676
77−From the web UI, click **Add Source** and paste an OpenAPI, GraphQL, or MCP URL.
77+From the web UI, click **Add Integration** and paste an OpenAPI, GraphQL, or MCP URL.
7878 Executor detects the type, indexes the tools, and handles auth. Or add one from the
7979 CLI:
8080
8181 ```bash
82−executor call executor openapi addSource '{
82+executor call executor openapi addIntegration '{
8383 "spec": "https://petstore3.swagger.io/api/v3/openapi.json",
8484 "namespace": "petstore",
8585 "baseUrl": "https://petstore3.swagger.io/api/v3"
@@ -91,7 +91,7 @@ Use `baseUrl` when the OpenAPI document has relative `servers` entries (e.g. `"/ · 5 unmodified lines
9191 Confirm it's live:
9292
9393 ```bash
94−executor tools sources # lists configured integrations + tool counts
94+executor tools integrations # lists configured integrations + tool counts
9595 ```
9696
9797 ## Call tools
apps/host-selfhost/src/integrations-mcp.test.tsrenamed from apps/host-selfhost/src/sources-mcp.test.ts+2−2
@@ -63,7 +63,7 @@ afterAll(() => dispose()); · 62 unmodified lines
6363
6464 const BASE = "http://localhost:4788";
6565
66−const addOrgSource = async (organizationId: string): Promise<void> => {
66+const addOrgIntegration = async (organizationId: string): Promise<void> => {
6767 // Register the integration and attach an org-owned connection, on its own
6868 // connection to the shared DB file. WAL makes the committed rows visible to
6969 // the server. Org-owned connections (and their per-connection tools) are
@@ -119,7 +119,7 @@ test("a user's MCP execute sandbox can reach an org-owned connection's tools", a · 49 unmodified lines
119119 );
120120 const organizationId = ((await meRes.json()) as { organization: { id: string } }).organization.id;
121121
122− await addOrgSource(organizationId);
122+ await addOrgIntegration(organizationId);
123123
124124 const mcp = (body: unknown, sessionId?: string) =>
125125 handler(
apps/host-selfhost/src/integrations.test.tsrenamed from apps/host-selfhost/src/sources.test.ts+0−0

File renamed without changes.

apps/local/src/auth-tool-failures.test.ts+2−2
@@ -71,7 +71,7 @@ const PingGroup = HttpApiGroup.make("default", { topLevel: true }).add( · 70 unmodified lines
7171 HttpApiEndpoint.get("ping", "/ping", { success: Schema.Unknown }),
7272 );
7373
74−const MissingAuthSourceApi = HttpApi.make("localAuthFailureSource").add(PingGroup);
74+const MissingAuthIntegrationApi = HttpApi.make("localAuthFailureIntegration").add(PingGroup);
7575
7676 const TestApi = addGroup(OpenApiGroup);
7777 type TestApiShape =
@@ -230,7 +230,7 @@ describe("local auth tool failures", () => { · 152 unmodified lines
230230 yield* run((client) =>
231231 client.openapi.addSpec({
232232 payload: {
233− ...makeOpenApiHttpApiTestAddSpecPayload(MissingAuthSourceApi, {
233+ ...makeOpenApiHttpApiTestAddSpecPayload(MissingAuthIntegrationApi, {
234234 slug: integration,
235235 authenticationTemplate: [
236236 {
apps/local/src/mcp-browser-resume.test.ts+1−1
@@ -44,7 +44,7 @@ const EmptyInputSchema = Schema.toStandardSchemaV1( · 43 unmodified lines
4444 const approvalPlugin = definePlugin(() => ({
4545 id: "browser-resume-test" as const,
4646 storage: () => ({}),
47− staticSources: () => [
47+ staticIntegrations: () => [
4848 {
4949 id: "api",
5050 kind: "in-memory",
apps/local/src/serve.test.ts+1−1
@@ -76,7 +76,7 @@ describe("startServer static/SPA routing (unauthenticated)", () => { · 75 unmodified lines
7676 const baseUrl = await startTestServer();
7777 // No Authorization header — the shell must still load so the browser can
7878 // read its `?_token` and authenticate subsequent /api calls.
79− const response = await fetch(`${baseUrl}/sources/add`);
79+ const response = await fetch(`${baseUrl}/integrations/add`);
8080
8181 expect(response.status).toBe(200);
8282 expect(response.headers.get("content-type")).toContain("text/html");
apps/marketing/src/pages/privacy.astro+2−2
@@ -39,7 +39,7 @@ import LegalLayout from "../components/LegalLayout.astro"; · 38 unmodified lines
3939 <ul>
4040 <li>organization and workspace names;</li>
4141 <li>membership and role information;</li>
42− <li>source and integration metadata;</li>
42+ <li>integration metadata;</li>
4343 <li>tool catalogs, schemas, policies, and related configuration;</li>
4444 <li>secret metadata and connection settings; and</li>
4545 <li>billing plan, membership, and execution-usage records.</li>
@@ -155,7 +155,7 @@ import LegalLayout from "../components/LegalLayout.astro"; · 109 unmodified lines
155155 </p>
156156
157157 <p>
158− You can also manage certain information directly in the product, such as organizations, memberships, sources,
158+ You can also manage certain information directly in the product, such as organizations, memberships,
159159 secrets, policies, and integrations. To exercise privacy rights or request account deletion, email
160160 <a href="mailto:rhys@executor.sh">rhys@executor.sh</a>.
161161 </p>
design.md+6−6
@@ -351,9 +351,9 @@ Do not use a uniform icon pack (Tabler, Lucide-as-decoration, Heroicons) or · 350 unmodified lines
351351 monogram-in-rounded-square placeholders: both read as generic, generated UI. Identity comes from
352352 authentic specifics or from nothing at all.
353353
354−- **App**: source and brand marks use the real favicon service,
354+- **App**: integration and brand marks use the real favicon service,
355355 `https://www.google.com/s2/favicons?domain={host}&sz={n}` (`integration-favicon.tsx`, rendered
356− 2x for retina), with Executor's own `/favicon-32.png` for the built-in source. The only
356+ 2x for retina), with Executor's own `/favicon-32.png` for the built-in integration. The only
357357 lucide import there is `BoxIcon` as a neutral fallback when a favicon fails to load.
358358 - **Marketing**: brand marks are local SVGs in `apps/marketing/src/assets/logos` (`github.svg`,
359359 `stripe.svg`, `linear.svg`, `claude.svg`, ...); the hub uses `/favicon-192.png`.
@@ -376,13 +376,13 @@ above.) · 16 unmodified lines
376376 Copy is part of the design; keep it precise, technical, and free of filler.
377377
378378 - Title Case for labels, buttons, titles, and tabs; sentence case for body, helper text, toasts.
379−- Name actions with a verb and a noun (`Add Source`, `Connect Agent`, `Revoke Token`), never
379+- Name actions with a verb and a noun (`Add Integration`, `Connect Agent`, `Revoke Token`), never
380380 `Confirm`, `OK`, or a bare verb.
381−- Errors are what happened plus what to do next: `Couldn't reach the source. Check the server is
381+- Errors are what happened plus what to do next: `Couldn't reach the integration. Check the server is
382382 running, then retry.`
383383 - Toasts name the specific thing that changed, drop the trailing period, never say
384− `successfully`: `Source added`, not `Successfully added the source.`
385−- Empty states point to the first action: `No sources yet. Add one to start sharing tools across
384+ `successfully`: `Integration added`, not `Successfully added the integration.`
385+- Empty states point to the first action: `No integrations yet. Add one to start sharing tools across
386386 your agents.`
387387 - Present participle with an ellipsis for in-progress states: `Connecting...`, `Syncing...`.
388388 - Use numerals (`3 tools`); skip `please` and marketing superlatives (powerful, seamless, robust,
e2e/cli/service-lifecycle.test.ts+2−2
@@ -76,7 +76,7 @@ scenario( · 75 unmodified lines
7676
7777 const listed = yield* chat.tool(
7878 {
79− name: "executor tools sources",
79+ name: "executor tools integrations",
8080 result: (rows) =>
8181 rows.map((r) => String(r.slug)).includes(slug) ? `${slug} is listed` : "NOT listed",
8282 },
@@ -103,7 +103,7 @@ scenario( · 20 unmodified lines
103103 Effect.gen(function* () {
104104 const survived = yield* chat.tool(
105105 {
106− name: "executor tools sources",
106+ name: "executor tools integrations",
107107 result: (rows) =>
108108 rows.map((r) => String(r.slug)).includes(slug)
109109 ? `${slug} SURVIVED the reboot`
e2e/cloud/auth-routing-flow.test.ts+1−1
@@ -41,7 +41,7 @@ scenario( · 40 unmodified lines
4141 yield* browser.session(anonymous, async ({ page, step }) => {
4242 await step("Signed out, the root redirects to /login", async () => {
4343 await page.goto("/", { waitUntil: "commit" });
44− await page.getByText("Sign in to manage your tools and sources").waitFor();
44+ await page.getByText("Sign in to manage your tools and integrations").waitFor();
4545 });
4646 expect(new URL(page.url()).pathname, "the signed-out root lands on /login").toBe("/login");
4747
e2e/cloud/cli-device-login.test.ts+3−3
@@ -6,7 +6,7 @@ · 5 unmodified lines
66 // URL, confirms the code, and clicks "Authorize device" on the WorkOS
77 // emulator's verification page, exactly the human hop, the way the MCP
88 // approval scenarios drive their browser step. The terminal then runs `whoami`
9−// and `tools sources`; a clean exit of that chain proves the resulting WorkOS
9+// and `tools integrations`; a clean exit of that chain proves the resulting WorkOS
1010 // access token (a JWT) is accepted by the protected `/api/*` plane.
1111 import { spawn } from "node:child_process";
1212 import { readFileSync } from "node:fs";
@@ -83,12 +83,12 @@ scenario( · 70 unmodified lines
8383
8484 // The terminal journey, recorded to terminal.cast. `&&` means a clean
8585 // exit only happens if every step, including the authenticated /api call
86− // (`tools sources`), succeeded.
86+ // (`tools integrations`), succeeded.
8787 const cli_ = `bun run ${CLI_ENTRY}`;
8888 const journey =
8989 `${cli_} login --base-url ${CLOUD_BASE_URL} --no-browser --name cloud && ` +
9090 `${cli_} whoami --server cloud && ` +
91− `${cli_} tools sources --server cloud`;
91+ `${cli_} tools integrations --server cloud`;
9292
9393 const terminal = cli.session(
9494 ["bash", "-c", journey],
e2e/cloud/integrations-api.test.tsrenamed from e2e/cloud/sources-api.test.ts+0−0

File renamed without changes.

e2e/cloud/integrations-refresh.test.tsrenamed from e2e/cloud/sources-refresh.test.ts+0−0

File renamed without changes.

e2e/cloud/oauth-callback-unauthenticated.test.ts+1−1
@@ -117,7 +117,7 @@ scenario( · 116 unmodified lines
117117 await step("Provider sends a signed-out browser to the OAuth callback", async () => {
118118 const response = await page.goto(callbackPath, { waitUntil: "commit" });
119119 expect(response?.status(), "the callback redirects into the login flow").toBe(200);
120− await page.getByText("Sign in to manage your tools and sources").waitFor();
120+ await page.getByText("Sign in to manage your tools and integrations").waitFor();
121121 });
122122
123123 const loginUrl = new URL(page.url());
e2e/cloud/toolkit-opencode-real.test.ts+3−3
@@ -1,5 +1,5 @@
11 // The toolkit-connect performance bug, reproduced with the REAL opencode
2−// binary in a REAL terminal. A user adds a dozen sources to their workspace,
2+// binary in a REAL terminal. A user adds a dozen integrations to their workspace,
33 // makes a toolkit, and points OpenCode at /mcp/toolkits/<slug>. OpenCode runs
44 // its own OAuth (discovery, DCR, PKCE) and then `mcp list`. The whole session
55 // runs in one recorded PTY: the run's terminal.cast replays exactly what a
@@ -37,8 +37,8 @@ scenario( · 31 unmodified lines
3737 const email = identity.credentials?.email ?? identity.label;
3838 const client = yield* makeClient(catalogApi, identity);
3939
40− // Build a production-shaped workspace: one real spec + ten more sources,
41− // a toolkit scoped to one of them. ~3,300 tools across 11 sources — large
40+ // Build a production-shaped workspace: one real spec + ten more integrations,
41+ // a toolkit scoped to one of them. ~3,300 tools across 11 integrations — large
4242 // enough that the pre-fix per-tool policy N+1 pushes the toolkit connect
4343 // past OpenCode's client timeout.
4444 const seeded = yield* seedLargeCatalog(client);
e2e/cloud/toolkit-policy-perf.test.ts+8−8
@@ -7,12 +7,12 @@ · 6 unmodified lines
77 // the policy engine over the WHOLE catalog to decide tool visibility. A
88 // per-tool policy resolution there is an N+1 that scales with total catalog
99 // size, not toolkit size: a workspace with thousands of tools across a dozen
10−// sources pushes the connect past the MCP client's connect timeout, and the
10+// integrations pushes the connect past the MCP client's connect timeout, and the
1111 // toolkit appears permanently "failed" even though nothing is broken.
1212 //
1313 // This scenario seeds a production-shaped catalog (one real OpenAPI spec plus
14−// enough synthetic sources to look like a working workspace, ~3,300 tools over
15−// 11 sources) and asserts the catalog size adds only a small, bounded cost to
14+// enough synthetic integrations to look like a working workspace, ~3,300 tools over
15+// 11 integrations) and asserts the catalog size adds only a small, bounded cost to
1616 // connect. The control is a fresh identity with a near-empty catalog: it pays
1717 // the same OAuth + MCP handshake, so the DIFFERENCE isolates the catalog-walk
1818 // cost the fix removes. Before the fix this delta is tens of seconds (and in
@@ -141,14 +141,14 @@ scenario( · 122 unmodified lines
141141 let seededCatalog: SeededCatalog | undefined;
142142
143143 yield* Effect.gen(function* () {
144− // 1 real source (Vercel, 322 ops) + 10 synthetic = 11 sources, ~2,200
144+ // 1 real integration (Vercel, 322 ops) + 10 synthetic = 11 integrations, ~2,200
145145 // tools. Sized so the pre-fix N+1 connect (~27s here) still completes
146146 // under the MCP client's connect timeout, so the regression surfaces as
147147 // a failed ASSERTION on the overhead rather than a connect crash.
148148 const seeded = yield* seedLargeCatalog(bigClient, {
149149 includeRealSpec: true,
150− syntheticSources: 10,
151− opsPerSource: 190,
150+ syntheticIntegrations: 10,
151+ opsPerIntegration: 190,
152152 });
153153 seededCatalog = seeded;
154154 expect(
@@ -157,7 +157,7 @@ scenario( · 2 unmodified lines
157157 ).toBeGreaterThan(2_000);
158158 expect(
159159 seeded.integrationSlugs.length,
160− "the catalog spans a production-like number of sources",
160+ "the catalog spans a production-like number of integrations",
161161 ).toBeGreaterThanOrEqual(11);
162162
163163 const big = yield* timeToolkitConnect(
@@ -172,7 +172,7 @@ scenario( · 8 unmodified lines
172172 const overhead = big.elapsedMs - control.elapsedMs;
173173 expect(
174174 overhead,
175− `catalog of ${seeded.toolCount} tools across ${seeded.integrationSlugs.length} sources ` +
175+ `catalog of ${seeded.toolCount} tools across ${seeded.integrationSlugs.length} integrations ` +
176176 `added ${overhead}ms to connect (big ${big.elapsedMs}ms vs control ${control.elapsedMs}ms); ` +
177177 `the per-tool policy N+1 would make this scale into tens of seconds`,
178178 ).toBeLessThan(MAX_CATALOG_CONNECT_OVERHEAD_MS);
e2e/cloud/unauthenticated-skeleton.test.ts+4−2
@@ -33,7 +33,7 @@ scenario( · 32 unmodified lines
3333
3434 await step("Open the cloud root while signed out → redirected to /login", async () => {
3535 await page.goto("/", { waitUntil: "commit" });
36− await page.getByText("Sign in to manage your tools and sources").waitFor();
36+ await page.getByText("Sign in to manage your tools and integrations").waitFor();
3737 });
3838
3939 // Snapshot DURING the /account/me window — where the skeleton used to be.
@@ -41,7 +41,9 @@ scenario( · 1 unmodified line
4141 url: new URL(page.url()).pathname,
4242 appShellSkeletons: await page.locator('[data-slot="skeleton"]').count(),
4343 sidebarShown: await page.locator("aside").first().isVisible(),
44− loginShown: await page.getByText("Sign in to manage your tools and sources").isVisible(),
44+ loginShown: await page
45+ .getByText("Sign in to manage your tools and integrations")
46+ .isVisible(),
4547 };
4648
4749 expect(
e2e/local/auth.test.ts+1−1
@@ -35,7 +35,7 @@ scenario( · 34 unmodified lines
3535 await step("Open the ?_token URL printed by executor web --foreground", async () => {
3636 await page.goto(url, { waitUntil: "domcontentloaded" });
3737 await page.getByRole("link", { name: "Secrets" }).first().waitFor({ timeout: 30_000 });
38− // Integrations actually LOAD (the built-in Executor source) — proves
38+ // Integrations actually LOAD (the built-in Executor integration) — proves
3939 // auth + data, not just the static shell.
4040 await page.getByText("built-in").first().waitFor({ timeout: 30_000 });
4141 // The token is moved out of the URL and persisted to localStorage.
e2e/local/update-notice.test.ts+1−1
@@ -87,7 +87,7 @@ scenario( · 86 unmodified lines
8787 await step("Open the console via the CLI's ?_token URL", async () => {
8888 await page.goto(url, { waitUntil: "domcontentloaded" });
8989 // The console renders past the auth gate: the Integrations page and
90− // its built-in Executor source row load (proves auth + data, not
90+ // its built-in Executor integration row load (proves auth + data, not
9191 // just the static shell).
9292 await page.getByRole("heading", { name: "Integrations" }).waitFor({ timeout: 60_000 });
9393 await page.getByText("Tool providers available").waitFor({ timeout: 30_000 });
e2e/scenarios/auth-methods.test.ts+4−4
@@ -4,7 +4,7 @@ · 3 unmodified lines
44 // each rendered from its own credential input; the merge-append configureAuth
55 // flow (a custom API key must never displace a detected OAuth method);
66 // declaring a method on a server that advertises none; GraphQL's multi-method
7−// add — and the EXTREME case end-to-end: a source declaring
7+// add — and the EXTREME case end-to-end: an integration declaring
88 // [oauth] [bearer header + team-id query] [bearer header] [query token],
99 // one connection per credential method, every invocation asserted against
1010 // what a real recording MCP server received.
@@ -192,7 +192,7 @@ scenario( · 181 unmodified lines
192192 );
193193
194194 scenario(
195− "Auth methods · a GraphQL source registers multiple auth methods at add time",
195+ "Auth methods · a GraphQL integration registers multiple auth methods at add time",
196196 {},
197197 Effect.gen(function* () {
198198 const target = yield* Target;
@@ -239,7 +239,7 @@ scenario( · 40 unmodified lines
239239 );
240240
241241 // ---------------------------------------------------------------------------
242−// THE EXTREME CASE — one source, four methods, three live connections, every
242+// THE EXTREME CASE — one integration, four methods, three live connections, every
243243 // invocation asserted against what the server actually received:
244244 // [oauth2] (declared; connect flow not exercised here)
245245 // [bearer header + team-id query] (TWO inputs on ONE method)
@@ -248,7 +248,7 @@ scenario( · 2 unmodified lines
248248 // ---------------------------------------------------------------------------
249249
250250 scenario(
251− "Auth methods · one source mixes oauth, a 2-input header+query method, a bearer header, and a query token",
251+ "Auth methods · one integration mixes oauth, a 2-input header+query method, a bearer header, and a query token",
252252 {},
253253 Effect.scoped(
254254 Effect.gen(function* () {
e2e/scenarios/microsoft-emulator.test.ts+1−1
@@ -133,7 +133,7 @@ scenario( · 132 unmodified lines
133133 );
134134 expect(
135135 appOnlyTemplate?.kind,
136− "the Microsoft source exposes an app-only OAuth method",
136+ "the Microsoft integration exposes an app-only OAuth method",
137137 ).toBe("oauth2");
138138 expect(
139139 appOnlyTemplate?.kind === "oauth2" ? appOnlyTemplate.scopes : [],
e2e/scenarios/microsoft-graph-default.test.ts+3−1
@@ -67,7 +67,9 @@ scenario( · 66 unmodified lines
6767 specFormat: "microsoft-graph",
6868 },
6969 });
70− expect(added.slug, "the Microsoft Graph source keeps the requested slug").toBe(integration);
70+ expect(added.slug, "the Microsoft Graph integration keeps the requested slug").toBe(
71+ integration,
72+ );
7173 expect(
7274 added.toolCount,
7375 "the Microsoft files catalog preset extracts a focused operation subtree",
e2e/scenarios/microsoft-graph-full.test.ts+3−1
@@ -65,7 +65,9 @@ scenario( · 64 unmodified lines
6565 specFormat: "microsoft-graph",
6666 },
6767 });
68− expect(added.slug, "the Microsoft files source keeps the requested slug").toBe(integration);
68+ expect(added.slug, "the Microsoft files integration keeps the requested slug").toBe(
69+ integration,
70+ );
6971 expect(
7072 added.toolCount,
7173 "adding the files catalog service extracts a focused Graph operation subtree",
e2e/scenarios/openapi-add-integration-action-bar.test.ts+4−4
@@ -1,4 +1,4 @@
1−// Cross-target (browser): the Add OpenAPI source form's floating action bar.
1+// Cross-target (browser): the Add OpenAPI integration form's floating action bar.
22 // Rohil (shared-tcc) reported the "Add integration" button rendering doubled /
33 // ghosted on click. This pins the flow: paste a spec, click Add integration,
44 // land on the created integration. The trace's DOM snapshots are the artifact
@@ -22,7 +22,7 @@ const singleServerSpec = JSON.stringify({ · 17 unmodified lines
2222 });
2323
2424 scenario(
25− "OpenAPI · Add integration commits the source and the action bar is a single node",
25+ "OpenAPI · Add integration commits the integration and the action bar is a single node",
2626 {},
2727 Effect.scoped(
2828 Effect.gen(function* () {
@@ -31,7 +31,7 @@ scenario( · 2 unmodified lines
3131 const identity = yield* target.newIdentity();
3232
3333 yield* browser.session(identity, async ({ page, step }) => {
34− await step("Open the Add OpenAPI source form", async () => {
34+ await step("Open the Add OpenAPI integration form", async () => {
3535 await page.goto("/integrations/add/openapi", { waitUntil: "networkidle" });
3636 await page.getByPlaceholder("https://api.example.com/openapi.json").waitFor();
3737 });
@@ -52,7 +52,7 @@ scenario( · 14 unmodified lines
5252 });
5353
5454 await step(
55− "Submitting commits the source and lands on the created integration",
55+ "Submitting commits the integration and lands on the created integration",
5656 async () => {
5757 // The reported ghost was the bar painting doubled when the submit
5858 // button changed width on click. The single-node counts (above and
e2e/scenarios/openapi-integration.test.tsrenamed from e2e/scenarios/openapi-source.test.ts+0−0

File renamed without changes.

e2e/scenarios/openapi-server-selection-ui.test.ts+2−2
@@ -1,5 +1,5 @@
11 // Cross-target (browser): the UI side of per-call server selection. When a
2−// pasted spec declares more than one server, the Add OpenAPI source form turns
2+// pasted spec declares more than one server, the Add OpenAPI integration form turns
33 // the Base URL field into a picker over those servers and relabels it an
44 // optional override — the host is otherwise resolved per tool call. The session
55 // video + per-step screenshots are the artifact; this scenario skips on targets
@@ -34,7 +34,7 @@ scenario( · 28 unmodified lines
3434 const identity = yield* target.newIdentity();
3535
3636 yield* browser.session(identity, async ({ page, step }) => {
37− await step("Open the Add OpenAPI source form", async () => {
37+ await step("Open the Add OpenAPI integration form", async () => {
3838 await page.goto("/integrations/add/openapi", { waitUntil: "networkidle" });
3939 await page.getByPlaceholder("https://api.example.com/openapi.json").waitFor();
4040 });
e2e/scenarios/support/large-catalog.ts+29−29
@@ -5,7 +5,7 @@ · 4 unmodified lines
55 // by running the policy engine over the WHOLE catalog on connect (the
66 // getDescription -> connections.list -> toolsList path), not just the toolkit's
77 // own connections. A per-tool resolution there is an N+1 that scales with total
8−// catalog size, so a realistic workspace (a real spec plus enough sources to
8+// catalog size, so a realistic workspace (a real spec plus enough integrations to
99 // look like one) is what surfaces the regression.
1010 //
1111 // `catalogApi` is exported so scenarios build their client from the SAME
@@ -65,7 +65,7 @@ export const syntheticSpec = (title: string, ops: number, baseUrl: string): stri · 53 unmodified lines
6565 });
6666 };
6767
68−export interface SeedSource {
68+export interface SeedIntegration {
6969 readonly slug: string;
7070 readonly specText: string;
7171 readonly baseUrl: string;
@@ -73,53 +73,53 @@ export interface SeedSource { · 1 unmodified line
7373 }
7474
7575 export interface SeedPlan {
76− readonly sources: ReadonlyArray<SeedSource>;
77− /** The first source's slug — the one the toolkit is scoped to. */
76+ readonly integrations: ReadonlyArray<SeedIntegration>;
77+ /** The first integration's slug — the one the toolkit is scoped to. */
7878 readonly firstSlug: string;
79− /** Connection pattern the toolkit binds (the first source, org, conn0). */
79+ /** Connection pattern the toolkit binds (the first integration, org, conn0). */
8080 readonly toolkitConnectionPattern: string;
8181 }
8282
8383 export interface SeedOptions {
84− /** Synthetic sources on top of the real spec. Default 10. */
85− readonly syntheticSources?: number;
86− /** Operations per synthetic source. Default 300. */
87− readonly opsPerSource?: number;
88− /** Include the real Vercel fixture (322 ops) as one source. Default true. */
84+ /** Synthetic integrations on top of the real spec. Default 10. */
85+ readonly syntheticIntegrations?: number;
86+ /** Operations per synthetic integration. Default 300. */
87+ readonly opsPerIntegration?: number;
88+ /** Include the real Vercel fixture (322 ops) as one integration. Default true. */
8989 readonly includeRealSpec?: boolean;
9090 }
9191
9292 /**
9393 * Plan a large, production-shaped catalog. With the defaults (1 real + 10
94− * synthetic sources, 300 ops each) it is ~3,300 tools across 11 sources, where
94+ * synthetic integrations, 300 ops each) it is ~3,300 tools across 11 integrations, where
9595 * the per-tool policy N+1 turns a toolkit connect from sub-second into a 30s+
96− * client timeout. Each source gets exactly one org connection at `conn0`.
96+ * client timeout. Each integration gets exactly one org connection at `conn0`.
9797 */
9898 export const planLargeCatalog = (options: SeedOptions = {}): SeedPlan => {
99− const syntheticSources = options.syntheticSources ?? 10;
100− const opsPerSource = options.opsPerSource ?? 300;
99+ const syntheticIntegrations = options.syntheticIntegrations ?? 10;
100+ const opsPerIntegration = options.opsPerIntegration ?? 300;
101101 const includeRealSpec = options.includeRealSpec ?? true;
102102
103− const sources: SeedSource[] = [];
103+ const integrations: SeedIntegration[] = [];
104104 if (includeRealSpec) {
105− sources.push({
105+ integrations.push({
106106 slug: unique("vercel"),
107107 specText: vercelSpecText(),
108108 baseUrl: "https://api.vercel.com",
109109 description: "Vercel API",
110110 });
111111 }
112− for (let s = 0; s < syntheticSources; s++) {
113− sources.push({
112+ for (let s = 0; s < syntheticIntegrations; s++) {
113+ integrations.push({
114114 slug: unique("svc"),
115− specText: syntheticSpec(`Service ${s}`, opsPerSource, "https://service.example"),
115+ specText: syntheticSpec(`Service ${s}`, opsPerIntegration, "https://service.example"),
116116 baseUrl: "https://service.example",
117117 });
118118 }
119119
120− const firstSlug = sources[0]!.slug;
120+ const firstSlug = integrations[0]!.slug;
121121 return {
122− sources,
122+ integrations,
123123 firstSlug,
124124 toolkitConnectionPattern: `${firstSlug}.org.conn0.*`,
125125 };
@@ -151,7 +151,7 @@ export interface SeededCatalog { · 25 unmodified lines
151151
152152 /**
153153 * Build the planned catalog under the current identity (one org connection per
154− * source) and a toolkit scoped to the first source, then report the total tool
154+ * integration) and a toolkit scoped to the first integration, then report the total tool
155155 * count and a finalizer. The toolkit's own surface is small; the connect cost
156156 * the scenarios guard comes from the policy engine walking the whole catalog.
157157 */
@@ -162,13 +162,13 @@ export const seedLargeCatalog = ( · 4 unmodified lines
162162 Effect.gen(function* () {
163163 const plan = planLargeCatalog(options);
164164
165− for (const source of plan.sources) {
165+ for (const integration of plan.integrations) {
166166 yield* client.openapi.addSpec({
167167 payload: {
168− spec: { kind: "blob", value: source.specText },
169− slug: IntegrationSlug.make(source.slug),
170− baseUrl: source.baseUrl,
171− ...(source.description ? { description: source.description } : {}),
168+ spec: { kind: "blob", value: integration.specText },
169+ slug: IntegrationSlug.make(integration.slug),
170+ baseUrl: integration.baseUrl,
171+ ...(integration.description ? { description: integration.description } : {}),
172172 authenticationTemplate: [
173173 {
174174 slug: "apiKey",
@@ -182,7 +182,7 @@ export const seedLargeCatalog = ( · 7 unmodified lines
182182 payload: {
183183 owner: "org",
184184 name: ConnectionName.make("conn0"),
185− integration: IntegrationSlug.make(source.slug),
185+ integration: IntegrationSlug.make(integration.slug),
186186 template: AuthTemplateSlug.make("apiKey"),
187187 value: "unused-token",
188188 },
@@ -200,7 +200,7 @@ export const seedLargeCatalog = ( · 11 unmodified lines
200200 payload: { pattern: plan.toolkitConnectionPattern },
201201 });
202202
203− const integrationSlugs = plan.sources.map((source) => source.slug);
203+ const integrationSlugs = plan.integrations.map((integration) => integration.slug);
204204 const cleanup = Effect.gen(function* () {
205205 yield* client.toolkits.remove({ params: { toolkitId: toolkit.id } }).pipe(Effect.ignore);
206206 yield* Effect.forEach(
e2e/scenarios/tool-descriptions.test.ts+2−2
@@ -426,7 +426,7 @@ scenario( · 425 unmodified lines
426426 "What an agent sees for tools derived from description-rich fixtures:",
427427 "the `tools.list` catalog entry (quoted) and the `tools.schema` view —",
428428 "the same compiled TypeScript previews `tools.describe.tool()` returns",
429− "inside the sandbox. Anything written in the fixture sources (bottom)",
429+ "inside the sandbox. Anything written in the fixture integrations (bottom)",
430430 "but absent here was dropped by the spec→tool pipeline.",
431431 "",
432432 ...sections.flatMap(([title, tools]) => [
@@ -442,7 +442,7 @@ scenario( · 9 unmodified lines
442442 "",
443443 codeBlock("md", inventory ?? "(no inventory section found)"),
444444 "",
445− "## Fixture sources",
445+ "## Fixture integrations",
446446 "",
447447 "<details><summary>OpenAPI spec</summary>",
448448 "",
e2e/selfhost/cli-device-login.test.ts+3−3
@@ -4,7 +4,7 @@ · 3 unmodified lines
44 // discovers Better Auth's device endpoints via /api/auth/cli-login, prints the
55 // verification URL, and polls. The browser leg is REAL (session.mp4): Playwright
66 // opens the self-host /device page (signed in via the session cookie) and clicks
7−// "Authorize device". The terminal then runs `whoami` and `tools sources`; a
7+// "Authorize device". The terminal then runs `whoami` and `tools integrations`; a
88 // clean exit of that chain proves the Better Auth device token is accepted as a
99 // Bearer on the protected /api/* plane.
1010 import { readFileSync } from "node:fs";
@@ -64,7 +64,7 @@ scenario( · 53 unmodified lines
6464 const journey =
6565 `${cli_} login --base-url ${SELFHOST_BASE_URL} --no-browser --name selfhost && ` +
6666 `${cli_} whoami --server selfhost && ` +
67− `${cli_} tools sources --server selfhost`;
67+ `${cli_} tools integrations --server selfhost`;
6868
6969 const terminal = cli.session(
7070 ["bash", "-c", journey],
@@ -113,7 +113,7 @@ scenario( · 42 unmodified lines
113113 });
114114
115115 // Reaching here means the whole `&&` chain exited 0, including the
116− // authenticated `tools sources` /api call.
116+ // authenticated `tools integrations` /api call.
117117 yield* Effect.all([terminal, browserApproval], { concurrency: "unbounded" });
118118
119119 // The stored profile carries an oauth device-login credential, not a key.
packages/app/src/web/shell.tsx+5−5
@@ -138,7 +138,7 @@ function IntegrationList(props: { pathname: string; onNavigate?: () => void }) { · 137 unmodified lines
138138 { id: slug, kind: integration.kind },
139139 integrationPlugins,
140140 )}
141− sourceId={slug}
141+ integrationId={slug}
142142 size="sm"
143143 />
144144 <span className="flex-1 truncate">{name}</span>
@@ -204,7 +204,7 @@ function SidebarContent(props: { · 59 unmodified lines
204204
205205 <PluginNav pathname={props.pathname} onNavigate={props.onNavigate} />
206206
207− {/* Sources list */}
207+ {/* Integrations list */}
208208 <Link
209209 to="/{-$orgSlug}"
210210 className="mt-5 mb-1 px-2.5 text-xs font-medium uppercase tracking-widest text-muted-foreground"
@@ -268,7 +268,7 @@ function SidebarContent(props: { · 57 unmodified lines
268268 export function Shell() {
269269 const location = useLocation();
270270 const pathname = location.pathname;
271− const refreshSources = useAtomRefresh(integrationsAtom);
271+ const refreshIntegrations = useAtomRefresh(integrationsAtom);
272272 const refreshTools = useAtomRefresh(toolsAllAtom);
273273 const lastPathname = useRef(pathname);
274274 const [mobileSidebarOpen, setMobileSidebarOpen] = useState(false);
@@ -294,7 +294,7 @@ export function Shell() { · 19 unmodified lines
294294 }
295295
296296 const refreshBackendData = () => {
297− refreshSources();
297+ refreshIntegrations();
298298 refreshTools();
299299 };
300300
@@ -303,7 +303,7 @@ export function Shell() { · 2 unmodified lines
303303 return () => {
304304 import.meta.hot?.off("executor:backend-updated", refreshBackendData);
305305 };
306− }, [refreshSources, refreshTools]);
306+ }, [refreshIntegrations, refreshTools]);
307307
308308 return (
309309 <div className="flex h-screen overflow-hidden">
packages/app/vite.ts+1−1
@@ -17,7 +17,7 @@ interface AppPluginOptions { · 16 unmodified lines
1717 * point this at their own (typically `apps/local/executor.config.ts`).
1818 *
1919 * If omitted, no plugin UIs get bundled — the renderer still works
20− * for built-in pages but Sources / Connections / etc. plugin pages
20+ * for built-in pages but Integrations / Connections / etc. plugin pages
2121 * stay empty.
2222 */
2323 readonly executorConfigPath?: string;
packages/core/api/src/plugin-routes.ts+1−1
@@ -57,7 +57,7 @@ export type PluginGroups<TPlugins extends readonly AnyPlugin[]> = ExtractPluginG · 56 unmodified lines
5757 TPlugins[number]
5858 >;
5959
60−/** Group identities baked into `CoreExecutorApi` (tools, sources, secrets,
60+/** Group identities baked into `CoreExecutorApi` (tools, integrations, secrets,
6161 * …). Extracted via inference so adding a core group flows through
6262 * without touching this helper. */
6363 type CoreGroups =
packages/core/config/src/config.test.ts+37−35
@@ -7,8 +7,8 @@ import { join } from "node:path"; · 6 unmodified lines
77 import { ExecutorFileConfig } from "./schema";
88 import { ConfigParseError, loadConfig } from "./load";
99 import {
10− addSourceToConfig,
11− removeSourceFromConfig,
10+ addIntegrationToConfig,
11+ removeIntegrationFromConfig,
1212 writeConfig,
1313 addSecretToConfig,
1414 removeSecretFromConfig,
@@ -27,15 +27,15 @@ const withTmpDir = <A, E>(fn: (dir: string) => Effect.Effect<A, E, FileSystem.Fi · 12 unmodified lines
2727
2828 describe("ExecutorFileConfig schema", () => {
2929 it("decodes a minimal config", () => {
30− const raw = { sources: [] };
30+ const raw = { integrations: [] };
3131 const result = decodeExecutorFileConfig(raw);
32− expect(result.sources).toEqual([]);
32+ expect(result.integrations).toEqual([]);
3333 });
3434
35− it("decodes a full config with all source types", () => {
35+ it("decodes a full config with all integration types", () => {
3636 const raw = {
3737 name: "test",
38− sources: [
38+ integrations: [
3939 {
4040 kind: "openapi",
4141 spec: "https://example.com/openapi.json",
@@ -73,14 +73,14 @@ describe("ExecutorFileConfig schema", () => { · 31 unmodified lines
7373 };
7474
7575 const result = decodeExecutorFileConfig(raw);
76− expect(result.sources).toHaveLength(4);
76+ expect(result.integrations).toHaveLength(4);
7777 expect(result.name).toBe("test");
7878 expect(result.secrets!["my-token"]!.name).toBe("My Token");
7979 });
8080
81− it("rejects invalid source kind", () => {
81+ it("rejects invalid integration kind", () => {
8282 const raw = {
83− sources: [{ kind: "invalid", endpoint: "http://example.com" }],
83+ integrations: [{ kind: "invalid", endpoint: "http://example.com" }],
8484 };
8585 expect(() => decodeExecutorFileConfig(raw)).toThrow();
8686 });
@@ -106,7 +106,7 @@ describe("loadConfig", () => { · 19 unmodified lines
106106 `{
107107 // This is a comment
108108 "name": "test",
109− "sources": [
109+ "integrations": [
110110 {
111111 "kind": "openapi",
112112 "spec": "https://example.com/spec.json"
@@ -118,7 +118,7 @@ describe("loadConfig", () => { · 5 unmodified lines
118118 const result = yield* loadConfig(path);
119119 expect(result).not.toBeNull();
120120 expect(result!.name).toBe("test");
121− expect(result!.sources).toHaveLength(1);
121+ expect(result!.integrations).toHaveLength(1);
122122 }),
123123 ),
124124 );
@@ -140,13 +140,13 @@ describe("loadConfig", () => { · 15 unmodified lines
140140 });
141141
142142 describe("write operations", () => {
143− it.effect("addSourceToConfig creates file and adds source", () =>
143+ it.effect("addIntegrationToConfig creates file and adds integration", () =>
144144 withTmpDir((dir) =>
145145 Effect.gen(function* () {
146146 const fs = yield* FileSystem.FileSystem;
147147 const path = join(dir, "executor.jsonc");
148148
149− yield* addSourceToConfig(path, {
149+ yield* addIntegrationToConfig(path, {
150150 kind: "openapi",
151151 spec: "https://example.com/spec.json",
152152 namespace: "example",
@@ -157,38 +157,38 @@ describe("write operations", () => { · 4 unmodified lines
157157 expect(content).toContain("example");
158158
159159 const config = yield* loadConfig(path);
160− expect(config!.sources).toHaveLength(1);
161− expect(config!.sources![0]!.kind).toBe("openapi");
160+ expect(config!.integrations).toHaveLength(1);
161+ expect(config!.integrations![0]!.kind).toBe("openapi");
162162 }),
163163 ),
164164 );
165165
166− it.effect("addSourceToConfig appends to existing sources", () =>
166+ it.effect("addIntegrationToConfig appends to existing integrations", () =>
167167 withTmpDir((dir) =>
168168 Effect.gen(function* () {
169169 const path = join(dir, "executor.jsonc");
170170
171− yield* addSourceToConfig(path, {
171+ yield* addIntegrationToConfig(path, {
172172 kind: "openapi",
173173 spec: "https://example.com/spec.json",
174174 namespace: "first",
175175 });
176176
177− yield* addSourceToConfig(path, {
177+ yield* addIntegrationToConfig(path, {
178178 kind: "graphql",
179179 endpoint: "https://example.com/graphql",
180180 namespace: "second",
181181 });
182182
183183 const config = yield* loadConfig(path);
184− expect(config!.sources).toHaveLength(2);
185− expect(config!.sources![0]!.kind).toBe("openapi");
186− expect(config!.sources![1]!.kind).toBe("graphql");
184+ expect(config!.integrations).toHaveLength(2);
185+ expect(config!.integrations![0]!.kind).toBe("openapi");
186+ expect(config!.integrations![1]!.kind).toBe("graphql");
187187 }),
188188 ),
189189 );
190190
191− it.effect("addSourceToConfig preserves comments", () =>
191+ it.effect("addIntegrationToConfig preserves comments", () =>
192192 withTmpDir((dir) =>
193193 Effect.gen(function* () {
194194 const fs = yield* FileSystem.FileSystem;
@@ -199,7 +199,7 @@ describe("write operations", () => { · 4 unmodified lines
199199 `{
200200 // My project config
201201 "name": "test",
202− "sources": [
202+ "integrations": [
203203 {
204204 "kind": "openapi",
205205 "spec": "https://example.com/spec.json",
@@ -209,7 +209,7 @@ describe("write operations", () => { · 3 unmodified lines
209209 }`,
210210 );
211211
212− yield* addSourceToConfig(path, {
212+ yield* addIntegrationToConfig(path, {
213213 kind: "graphql",
214214 endpoint: "https://example.com/graphql",
215215 namespace: "second",
@@ -222,19 +222,19 @@ describe("write operations", () => { · 6 unmodified lines
222222 ),
223223 );
224224
225− it.effect("addSourceToConfig replaces existing source with same namespace", () =>
225+ it.effect("addIntegrationToConfig replaces existing integration with same namespace", () =>
226226 withTmpDir((dir) =>
227227 Effect.gen(function* () {
228228 const path = join(dir, "executor.jsonc");
229229
230− yield* addSourceToConfig(path, {
230+ yield* addIntegrationToConfig(path, {
231231 kind: "openapi",
232232 spec: "https://example.com/v1.json",
233233 namespace: "example",
234234 });
235235
236236 // Add again with same namespace but different spec
237− yield* addSourceToConfig(path, {
237+ yield* addIntegrationToConfig(path, {
238238 kind: "openapi",
239239 spec: "https://example.com/v2.json",
240240 namespace: "example",
@@ -242,19 +242,21 @@ describe("write operations", () => { · 1 unmodified line
242242
243243 const config = yield* loadConfig(path);
244244 // Should have 1, not 2
245− expect(config!.sources).toHaveLength(1);
246− expect((config!.sources![0] as { spec: string }).spec).toBe("https://example.com/v2.json");
245+ expect(config!.integrations).toHaveLength(1);
246+ expect((config!.integrations![0] as { spec: string }).spec).toBe(
247+ "https://example.com/v2.json",
248+ );
247249 }),
248250 ),
249251 );
250252
251− it.effect("removeSourceFromConfig removes by namespace", () =>
253+ it.effect("removeIntegrationFromConfig removes by namespace", () =>
252254 withTmpDir((dir) =>
253255 Effect.gen(function* () {
254256 const path = join(dir, "executor.jsonc");
255257
256258 yield* writeConfig(path, {
257− sources: [
259+ integrations: [
258260 {
259261 kind: "openapi",
260262 spec: "https://example.com/spec.json",
@@ -268,11 +270,11 @@ describe("write operations", () => { · 7 unmodified lines
268270 ],
269271 });
270272
271− yield* removeSourceFromConfig(path, "remove-me");
273+ yield* removeIntegrationFromConfig(path, "remove-me");
272274
273275 const config = yield* loadConfig(path);
274− expect(config!.sources).toHaveLength(1);
275− expect(config!.sources![0]!.kind).toBe("openapi");
276+ expect(config!.integrations).toHaveLength(1);
277+ expect(config!.integrations![0]!.kind).toBe("openapi");
276278 }),
277279 ),
278280 );
@@ -282,7 +284,7 @@ describe("write operations", () => { · 3 unmodified lines
282284 Effect.gen(function* () {
283285 const path = join(dir, "executor.jsonc");
284286
285− yield* writeConfig(path, { sources: [] });
287+ yield* writeConfig(path, { integrations: [] });
286288
287289 yield* addSecretToConfig(path, "my-token", {
288290 name: "My Token",
packages/core/config/src/index.ts+7−7
@@ -1,11 +1,11 @@
11 export {
22 ExecutorFileConfig,
33 PluginConfig,
4− SourceConfig,
5− OpenApiSourceConfig,
6− GraphqlSourceConfig,
7− McpRemoteSourceConfig,
8− McpStdioSourceConfig,
4+ IntegrationConfig,
5+ OpenApiIntegrationConfig,
6+ GraphqlIntegrationConfig,
7+ McpRemoteIntegrationConfig,
8+ McpStdioIntegrationConfig,
99 McpAuthConfig,
1010 SecretMetadata,
1111 ConfigHeaderValue,
@@ -17,8 +17,8 @@ export { loadPluginsFromJsonc } from "./load-plugins"; · 5 unmodified lines
1717 export type { LoadPluginsFromJsoncOptions } from "./load-plugins";
1818
1919 export {
20− addSourceToConfig,
21− removeSourceFromConfig,
20+ addIntegrationToConfig,
21+ removeIntegrationFromConfig,
2222 writeConfig,
2323 addSecretToConfig,
2424 removeSecretFromConfig,
packages/core/config/src/schema.ts+16−16
@@ -23,26 +23,26 @@ export type ConfigHeaderValue = typeof ConfigHeaderValue.Type; · 22 unmodified lines
2323 const ConfigHeaders = Schema.Record(Schema.String, ConfigHeaderValue);
2424
2525 // ---------------------------------------------------------------------------
26−// Source configs — discriminated union on "kind"
26+// Integration configs — discriminated union on "kind"
2727 // ---------------------------------------------------------------------------
2828
29−export const OpenApiSourceConfig = Schema.Struct({
29+export const OpenApiIntegrationConfig = Schema.Struct({
3030 kind: Schema.Literal("openapi"),
3131 spec: Schema.String,
3232 baseUrl: Schema.optional(Schema.String),
3333 namespace: Schema.optional(Schema.String),
3434 headers: Schema.optional(ConfigHeaders),
3535 });
36−export type OpenApiSourceConfig = typeof OpenApiSourceConfig.Type;
36+export type OpenApiIntegrationConfig = typeof OpenApiIntegrationConfig.Type;
3737
38−export const GraphqlSourceConfig = Schema.Struct({
38+export const GraphqlIntegrationConfig = Schema.Struct({
3939 kind: Schema.Literal("graphql"),
4040 endpoint: Schema.String,
4141 introspectionJson: Schema.optional(Schema.NullOr(Schema.String)),
4242 namespace: Schema.optional(Schema.String),
4343 headers: Schema.optional(ConfigHeaders),
4444 });
45−export type GraphqlSourceConfig = typeof GraphqlSourceConfig.Type;
45+export type GraphqlIntegrationConfig = typeof GraphqlIntegrationConfig.Type;
4646
4747 const StringMap = Schema.Record(Schema.String, Schema.String);
4848
@@ -66,7 +66,7 @@ export const McpAuthConfig = Schema.Union([ · 17 unmodified lines
6666 ]);
6767 export type McpAuthConfig = typeof McpAuthConfig.Type;
6868
69−export const McpRemoteSourceConfig = Schema.Struct({
69+export const McpRemoteIntegrationConfig = Schema.Struct({
7070 kind: Schema.Literal("mcp"),
7171 transport: Schema.Literal("remote"),
7272 name: Schema.String,
@@ -77,9 +77,9 @@ export const McpRemoteSourceConfig = Schema.Struct({ · 4 unmodified lines
7777 headers: Schema.optional(ConfigHeaders),
7878 auth: Schema.optional(McpAuthConfig),
7979 });
80−export type McpRemoteSourceConfig = typeof McpRemoteSourceConfig.Type;
80+export type McpRemoteIntegrationConfig = typeof McpRemoteIntegrationConfig.Type;
8181
82−export const McpStdioSourceConfig = Schema.Struct({
82+export const McpStdioIntegrationConfig = Schema.Struct({
8383 kind: Schema.Literal("mcp"),
8484 transport: Schema.Literal("stdio"),
8585 name: Schema.String,
@@ -89,15 +89,15 @@ export const McpStdioSourceConfig = Schema.Struct({ · 3 unmodified lines
8989 cwd: Schema.optional(Schema.String),
9090 namespace: Schema.optional(Schema.String),
9191 });
92−export type McpStdioSourceConfig = typeof McpStdioSourceConfig.Type;
92+export type McpStdioIntegrationConfig = typeof McpStdioIntegrationConfig.Type;
9393
94−export const SourceConfig = Schema.Union([
95− OpenApiSourceConfig,
96− GraphqlSourceConfig,
97− McpRemoteSourceConfig,
98− McpStdioSourceConfig,
94+export const IntegrationConfig = Schema.Union([
95+ OpenApiIntegrationConfig,
96+ GraphqlIntegrationConfig,
97+ McpRemoteIntegrationConfig,
98+ McpStdioIntegrationConfig,
9999 ]);
100−export type SourceConfig = typeof SourceConfig.Type;
100+export type IntegrationConfig = typeof IntegrationConfig.Type;
101101
102102 // ---------------------------------------------------------------------------
103103 // Secret metadata
@@ -136,7 +136,7 @@ export const ExecutorFileConfig = Schema.Struct({ · 32 unmodified lines
136136 $schema: Schema.optional(Schema.String),
137137 name: Schema.optional(Schema.String),
138138 plugins: Schema.optional(Schema.Array(PluginConfig)),
139− sources: Schema.optional(Schema.Array(SourceConfig)),
139+ integrations: Schema.optional(Schema.Array(IntegrationConfig)),
140140 secrets: Schema.optional(Schema.Record(Schema.String, SecretMetadata)),
141141 });
142142 export type ExecutorFileConfig = typeof ExecutorFileConfig.Type;
packages/core/config/src/sink.ts+10−10
@@ -1,7 +1,7 @@
11 // ---------------------------------------------------------------------------
2−// ConfigFileSink — best-effort write-back of source changes to executor.jsonc.
2+// ConfigFileSink — best-effort write-back of integration changes to executor.jsonc.
33 //
4−// Plugins (openapi, graphql, mcp) call `sink.upsertSource` after their DB
4+// Plugins (openapi, graphql, mcp) call `sink.upsertIntegration` after their DB
55 // writes so the committable file stays in sync with runtime state. Errors
66 // are logged and swallowed — a failed file write must never fail a DB
77 // mutation, and the next successful mutation (or a boot-time sync) will
@@ -15,8 +15,8 @@ import { Effect } from "effect"; · 7 unmodified lines
1515 import type { Layer } from "effect";
1616 import type { FileSystem } from "effect";
1717
18−import { SECRET_REF_PREFIX, type ConfigHeaderValue, type SourceConfig } from "./schema";
19−import { addSourceToConfig, removeSourceFromConfig } from "./write";
18+import { SECRET_REF_PREFIX, type ConfigHeaderValue, type IntegrationConfig } from "./schema";
19+import { addIntegrationToConfig, removeIntegrationFromConfig } from "./write";
2020
2121 // Translate a plugin-side header value (`{ secretId, prefix? }` for secret
2222 // refs) into the config file's `secret-public-ref:<id>` string form.
@@ -38,8 +38,8 @@ export const headersToConfigValues = ( · 15 unmodified lines
3838 };
3939
4040 export interface ConfigFileSink {
41− readonly upsertSource: (source: SourceConfig) => Effect.Effect<void>;
42− readonly removeSource: (namespace: string) => Effect.Effect<void>;
41+ readonly upsertIntegration: (integration: IntegrationConfig) => Effect.Effect<void>;
42+ readonly removeIntegration: (namespace: string) => Effect.Effect<void>;
4343 }
4444
4545 export interface ConfigFileSinkOptions {
@@ -57,14 +57,14 @@ export const makeFileConfigSink = (options: ConfigFileSinkOptions): ConfigFileSi · 11 unmodified lines
5757 const { path, fsLayer, onError = defaultOnError } = options;
5858
5959 return {
60− upsertSource: (source) =>
61− addSourceToConfig(path, source).pipe(
60+ upsertIntegration: (integration) =>
61+ addIntegrationToConfig(path, integration).pipe(
6262 Effect.provide(fsLayer),
6363 Effect.catch((err: unknown) => Effect.sync(() => onError("upsert", err))),
6464 ),
6565
66− removeSource: (namespace) =>
67− removeSourceFromConfig(path, namespace).pipe(
66+ removeIntegration: (namespace) =>
67+ removeIntegrationFromConfig(path, namespace).pipe(
6868 Effect.provide(fsLayer),
6969 Effect.catch((err: unknown) => Effect.sync(() => onError("remove", err))),
7070 ),
packages/core/config/src/write.ts+24−24
@@ -2,7 +2,7 @@ import { Effect } from "effect"; · 1 unmodified line
22 import { FileSystem } from "effect";
33 import type { PlatformError } from "effect/PlatformError";
44 import * as jsonc from "jsonc-parser";
5−import type { SourceConfig, ExecutorFileConfig } from "./schema";
5+import type { IntegrationConfig, ExecutorFileConfig } from "./schema";
66
77 export class ConfigWriteError {
88 readonly _tag = "ConfigWriteError";
@@ -19,7 +19,7 @@ const FORMATTING: jsonc.FormattingOptions = { · 10 unmodified lines
1919 };
2020
2121 const DEFAULT_CONFIG = `{
22− "sources": []
22+ "integrations": []
2323 }
2424 `;
2525
@@ -36,35 +36,35 @@ const readOrCreate = ( · 10 unmodified lines
3636 });
3737
3838 /**
39− * Add a source entry to the config file. Creates the file if it doesn't exist.
39+ * Add an integration entry to the config file. Creates the file if it doesn't exist.
4040 * Preserves existing comments and formatting.
4141 */
42−export const addSourceToConfig = (
42+export const addIntegrationToConfig = (
4343 path: string,
44− source: SourceConfig,
44+ integration: IntegrationConfig,
4545 ): Effect.Effect<void, PlatformError, FileSystem.FileSystem> =>
4646 Effect.gen(function* () {
4747 const fs = yield* FileSystem.FileSystem;
4848 let text = yield* readOrCreate(fs, path);
4949
50− // Ensure "sources" array exists
50+ // Ensure "integrations" array exists
5151 let tree = jsonc.parseTree(text);
52− let sourcesNode = tree ? jsonc.findNodeAtLocation(tree, ["sources"]) : undefined;
52+ let integrationsNode = tree ? jsonc.findNodeAtLocation(tree, ["integrations"]) : undefined;
5353
54− if (!sourcesNode) {
55− const edits = jsonc.modify(text, ["sources"], [source], {
54+ if (!integrationsNode) {
55+ const edits = jsonc.modify(text, ["integrations"], [integration], {
5656 formattingOptions: FORMATTING,
5757 });
5858 text = jsonc.applyEdits(text, edits);
5959 } else {
6060 // Remove existing entry with same namespace (if any) to avoid duplicates
61− const ns = "namespace" in source ? source.namespace : undefined;
62− if (ns && sourcesNode.children) {
63− for (let i = sourcesNode.children.length - 1; i >= 0; i--) {
64− const child = sourcesNode.children[i]!;
61+ const ns = "namespace" in integration ? integration.namespace : undefined;
62+ if (ns && integrationsNode.children) {
63+ for (let i = integrationsNode.children.length - 1; i >= 0; i--) {
64+ const child = integrationsNode.children[i]!;
6565 const nsNode = jsonc.findNodeAtLocation(child, ["namespace"]);
6666 if (nsNode && jsonc.getNodeValue(nsNode) === ns) {
67− const edits = jsonc.modify(text, ["sources", i], undefined, {
67+ const edits = jsonc.modify(text, ["integrations", i], undefined, {
6868 formattingOptions: FORMATTING,
6969 });
7070 text = jsonc.applyEdits(text, edits);
@@ -72,11 +72,11 @@ export const addSourceToConfig = ( · 1 unmodified line
7272 }
7373 // Re-parse after removals
7474 tree = jsonc.parseTree(text);
75− sourcesNode = tree ? jsonc.findNodeAtLocation(tree, ["sources"]) : undefined;
75+ integrationsNode = tree ? jsonc.findNodeAtLocation(tree, ["integrations"]) : undefined;
7676 }
7777
78− const count = sourcesNode?.children?.length ?? 0;
79− const edits = jsonc.modify(text, ["sources", count], source, {
78+ const count = integrationsNode?.children?.length ?? 0;
79+ const edits = jsonc.modify(text, ["integrations", count], integration, {
8080 formattingOptions: FORMATTING,
8181 });
8282 text = jsonc.applyEdits(text, edits);
@@ -86,9 +86,9 @@ export const addSourceToConfig = ( · 3 unmodified lines
8686 });
8787
8888 /**
89− * Remove a source from the config file by namespace.
89+ * Remove an integration from the config file by namespace.
9090 */
91−export const removeSourceFromConfig = (
91+export const removeIntegrationFromConfig = (
9292 path: string,
9393 namespace: string,
9494 ): Effect.Effect<void, PlatformError, FileSystem.FileSystem> =>
@@ -102,15 +102,15 @@ export const removeSourceFromConfig = ( · 7 unmodified lines
102102 const tree = jsonc.parseTree(text);
103103 if (!tree) return;
104104
105− const sourcesNode = jsonc.findNodeAtLocation(tree, ["sources"]);
106− if (!sourcesNode?.children) return;
105+ const integrationsNode = jsonc.findNodeAtLocation(tree, ["integrations"]);
106+ if (!integrationsNode?.children) return;
107107
108108 // Walk backwards so indices stay valid after each removal
109− for (let i = sourcesNode.children.length - 1; i >= 0; i--) {
110− const child = sourcesNode.children[i]!;
109+ for (let i = integrationsNode.children.length - 1; i >= 0; i--) {
110+ const child = integrationsNode.children[i]!;
111111 const nsNode = jsonc.findNodeAtLocation(child, ["namespace"]);
112112 if (nsNode && jsonc.getNodeValue(nsNode) === namespace) {
113− const edits = jsonc.modify(text, ["sources", i], undefined, {
113+ const edits = jsonc.modify(text, ["integrations", i], undefined, {
114114 formattingOptions: FORMATTING,
115115 });
116116 text = jsonc.applyEdits(text, edits);
packages/core/execution/src/engine.test.ts+1−1
@@ -31,7 +31,7 @@ const succeedingExecutor: CodeExecutor<FakeRuntimeError> = { · 30 unmodified lines
3131 const emptyPlugin = definePlugin(() => ({
3232 id: "empty-test" as const,
3333 storage: () => ({}),
34− staticSources: () => [],
34+ staticIntegrations: () => [],
3535 }));
3636
3737 const makeExecutor = () => createExecutor(makeTestConfig({ plugins: [emptyPlugin()] as const }));
packages/core/execution/src/engine.ts+7−7
@@ -15,7 +15,7 @@ import type { CodeExecutor, ExecuteResult, SandboxToolInvoker } from "@executor- · 14 unmodified lines
1515 import {
1616 defaultToolDiscoveryProvider,
1717 makeExecutorToolInvoker,
18− listExecutorSources,
18+ listExecutorIntegrations,
1919 describeTool,
2020 type ToolDiscoveryProvider,
2121 } from "./tool-invoker";
@@ -298,12 +298,12 @@ const makeFullInvoker = ( · 276 unmodified lines
298298 }),
299299 );
300300 }
301− if (path === "executor.sources.list") {
301+ if (path === "executor.integrations.list") {
302302 if (args !== undefined && !isRecord(args)) {
303303 return Effect.fail(
304304 new ExecutionToolError({
305305 message:
306− "tools.executor.sources.list expects an object: { query?: string; limit?: number; offset?: number }",
306+ "tools.executor.integrations.list expects an object: { query?: string; limit?: number; offset?: number }",
307307 }),
308308 );
309309 }
@@ -311,14 +311,14 @@ const makeFullInvoker = ( · 1 unmodified line
311311 if (isRecord(args) && args.query !== undefined && typeof args.query !== "string") {
312312 return Effect.fail(
313313 new ExecutionToolError({
314− message: "tools.executor.sources.list query must be a string when provided",
314+ message: "tools.executor.integrations.list query must be a string when provided",
315315 }),
316316 );
317317 }
318318
319319 const limit = readOptionalLimit(
320320 isRecord(args) ? args.limit : undefined,
321− "tools.executor.sources.list",
321+ "tools.executor.integrations.list",
322322 );
323323 if (Predicate.isTagged(limit, "ExecutionToolError")) {
324324 return Effect.fail(limit);
@@ -326,13 +326,13 @@ const makeFullInvoker = ( · 1 unmodified line
326326
327327 const offset = readOptionalOffset(
328328 isRecord(args) ? args.offset : undefined,
329− "tools.executor.sources.list",
329+ "tools.executor.integrations.list",
330330 );
331331 if (Predicate.isTagged(offset, "ExecutionToolError")) {
332332 return Effect.fail(offset);
333333 }
334334
335− return listExecutorSources(executor, {
335+ return listExecutorIntegrations(executor, {
336336 query: isRecord(args) && typeof args.query === "string" ? args.query : undefined,
337337 limit,
338338 offset,
packages/core/execution/src/index.ts+1−1
@@ -18,7 +18,7 @@ export { · 17 unmodified lines
1818 defaultToolDiscoveryProvider,
1919 makeExecutorToolInvoker,
2020 searchTools,
21− listExecutorSources,
21+ listExecutorIntegrations,
2222 describeTool,
2323 type ToolDiscoveryInput,
2424 type ToolDiscoveryProvider,
packages/core/execution/src/tool-invoker.leak.test.ts+1−1
@@ -34,7 +34,7 @@ class FakeOpenApiInvocationError extends Data.TaggedError("OpenApiInvocationErro · 33 unmodified lines
3434 const leakyPlugin = definePlugin(() => ({
3535 id: "leaky-test" as const,
3636 storage: () => ({}),
37− staticSources: () => [
37+ staticIntegrations: () => [
3838 {
3939 id: "leaky",
4040 kind: "in-memory",
packages/core/execution/src/tool-invoker.test.ts+11−11
@@ -630,7 +630,7 @@ describe("tool discovery", () => { · 629 unmodified lines
630630 const executor = yield* makeSearchExecutor();
631631
632632 const listed = yield* createExecutionEngine({ executor, codeExecutor }).execute(
633− "return await tools.executor.sources.list();",
633+ "return await tools.executor.integrations.list();",
634634 { onElicitation: acceptAll },
635635 );
636636 expect(listed.error).toBeUndefined();
@@ -666,7 +666,7 @@ describe("tool discovery", () => { · 29 unmodified lines
666666
667667 // total = 2 (github, crm), sorted by id ("crm" < "github")
668668 const firstPage = yield* engine.execute(
669− "return await tools.executor.sources.list({ limit: 1 });",
669+ "return await tools.executor.integrations.list({ limit: 1 });",
670670 { onElicitation: acceptAll },
671671 );
672672 expect(firstPage.error).toBeUndefined();
@@ -680,7 +680,7 @@ describe("tool discovery", () => { · 7 unmodified lines
680680 );
681681
682682 const secondPage = yield* engine.execute(
683− "return await tools.executor.sources.list({ limit: 1, offset: 1 });",
683+ "return await tools.executor.integrations.list({ limit: 1, offset: 1 });",
684684 { onElicitation: acceptAll },
685685 );
686686 expect(secondPage.error).toBeUndefined();
@@ -719,7 +719,7 @@ describe("tool discovery", () => { · 32 unmodified lines
719719 const badList = yield* engine.execute(
720720 [
721721 "try {",
722− " await tools.executor.sources.list({ offset: -5 });",
722+ " await tools.executor.integrations.list({ offset: -5 });",
723723 ' return "unexpected";',
724724 "} catch (error) {",
725725 " return error instanceof Error ? error.message : String(error);",
@@ -729,7 +729,7 @@ describe("tool discovery", () => { · 3 unmodified lines
729729 );
730730 expect(badList.error).toBeUndefined();
731731 expect(String(badList.result)).toContain(
732− "tools.executor.sources.list offset must be a non-negative number when provided",
732+ "tools.executor.integrations.list offset must be a non-negative number when provided",
733733 );
734734 }),
735735 );
@@ -876,13 +876,13 @@ describe("tool discovery", () => { · 140 unmodified lines
876876 const execution = yield* engine.execute(
877877 [
878878 "const searchDetails = await tools.describe.tool({ path: 'search' });",
879− "const sourceDetails = await tools.describe.tool({ path: 'executor.sources.list' });",
879+ "const integrationDetails = await tools.describe.tool({ path: 'executor.integrations.list' });",
880880 "const describeDetails = await tools.describe.tool({ path: 'describe.tool' });",
881881 "return {",
882882 " searchDetails,",
883883 " searchResult: await tools.search({ query: 'repo details', limit: 2 }),",
884− " sourceDetails,",
885− " sourceResult: await tools.executor.sources.list({ limit: 2 }),",
884+ " integrationDetails,",
885+ " integrationResult: await tools.executor.integrations.list({ limit: 2 }),",
886886 " describeDetails,",
887887 " describeResult: await tools.describe.tool({ path: 'github.org.main.getRepositoryDetails' }),",
888888 "};",
@@ -894,8 +894,8 @@ describe("tool discovery", () => { · 5 unmodified lines
894894 const observed = execution.result as {
895895 readonly searchDetails: DescribedToolContract;
896896 readonly searchResult: unknown;
897− readonly sourceDetails: DescribedToolContract;
898− readonly sourceResult: unknown;
897+ readonly integrationDetails: DescribedToolContract;
898+ readonly integrationResult: unknown;
899899 readonly describeDetails: DescribedToolContract;
900900 readonly describeResult: unknown;
901901 };
@@ -904,7 +904,7 @@ describe("tool discovery", () => { · 2 unmodified lines
904904 typeCheckDescribedInvocation(observed.searchDetails, observed.searchResult, ""),
905905 ).toEqual([]);
906906 expect(
907− typeCheckDescribedInvocation(observed.sourceDetails, observed.sourceResult, ""),
907+ typeCheckDescribedInvocation(observed.integrationDetails, observed.integrationResult, ""),
908908 ).toEqual([]);
909909 expect(
910910 typeCheckDescribedInvocation(observed.describeDetails, observed.describeResult, ""),
packages/core/execution/src/tool-invoker.ts+17−17
@@ -105,16 +105,16 @@ const BUILTIN_TOOL_DESCRIPTIONS: ReadonlyMap<string, DescribedTool> = new Map< · 104 unmodified lines
105105 },
106106 ],
107107 [
108− "executor.sources.list",
108+ "executor.integrations.list",
109109 {
110− path: "executor.sources.list",
111− name: "executor.sources.list",
110+ path: "executor.integrations.list",
111+ name: "executor.integrations.list",
112112 description: "List configured Executor integrations.",
113113 inputTypeScript: "{ query?: string; limit?: number; offset?: number; }",
114114 outputTypeScript:
115− "{ items: ExecutorSourceListItem[]; total: number; hasMore: boolean; nextOffset: number | null; }",
115+ "{ items: ExecutorIntegrationListItem[]; total: number; hasMore: boolean; nextOffset: number | null; }",
116116 typeScriptDefinitions: {
117− ExecutorSourceListItem:
117+ ExecutorIntegrationListItem:
118118 "{ id: string; name: string; description?: string; kind: string; canRemove?: boolean; canRefresh?: boolean; toolCount: number; }",
119119 },
120120 },
@@ -245,7 +245,7 @@ const expectedToolFailure = (value: unknown): ToolError | null => { · 124 unmodified lines
245245 /**
246246 * Extract the integration namespace from a tool path. v2 addresses look like
247247 * `<integration>.<owner>.<connection>.<tool>`; static fqids look like
248− * `<source>.<op>`. We take the first segment as a cheap, non-lookup namespace
248+ * `<integration>.<op>`. We take the first segment as a cheap, non-lookup namespace
249249 * for the span attribute so it's always populated without a catalog read.
250250 */
251251 const extractNamespace = (path: string): string => {
@@ -364,7 +364,7 @@ export type ToolDiscoveryResult = { · 112 unmodified lines
364364 readonly score: number;
365365 };
366366
367−export type ExecutorSourceListItem = {
367+export type ExecutorIntegrationListItem = {
368368 readonly id: string;
369369 readonly name: string;
370370 readonly description?: string;
@@ -390,7 +390,7 @@ export interface ToolDiscoveryProvider { · 19 unmodified lines
390390
391391 /**
392392 * Page of results from a list-style discovery tool. Shared by
393− * `tools.search` and `tools.executor.sources.list` so the model sees one
393+ * `tools.search` and `tools.executor.integrations.list` so the model sees one
394394 * consistent shape:
395395 *
396396 * - `items` — the page (slice).
@@ -674,10 +674,10 @@ export const defaultToolDiscoveryProvider: ToolDiscoveryProvider = { · 277 unmodified lines
674674 searchTools(executor, query, limit, { namespace, offset }),
675675 };
676676
677−/** What `tools.executor.sources.list()` calls inside the sandbox. v2: the
678− * "sources" are the integration catalog; tool counts come from the
677+/** What `tools.executor.integrations.list()` calls inside the sandbox. v2: the
678+ * integrations are the integration catalog; tool counts come from the
679679 * per-connection tool list. */
680−export const listExecutorSources = Effect.fn("executor.sources.list")(function* (
680+export const listExecutorIntegrations = Effect.fn("executor.integrations.list")(function* (
681681 executor: Executor,
682682 options?: {
683683 readonly query?: string;
@@ -731,7 +731,7 @@ export const listExecutorSources = Effect.fn("executor.sources.list")(function* · 47 unmodified lines
731731 id: String(integration.slug),
732732 name: String(integration.slug),
733733 // The integration's catalog description — user-editable context the
734− // agent can use to pick a source. Omitted when it just repeats the
734+ // agent can use to pick an integration. Omitted when it just repeats the
735735 // slug or display name (no information beyond identity).
736736 ...(integration.description &&
737737 integration.description.toLowerCase() !== String(integration.slug).toLowerCase() &&
@@ -742,17 +742,17 @@ export const listExecutorSources = Effect.fn("executor.sources.list")(function* · 4 unmodified lines
742742 canRemove: integration.canRemove,
743743 canRefresh: integration.canRefresh,
744744 toolCount: toolCountByIntegration.get(String(integration.slug)) ?? 0,
745− }) satisfies ExecutorSourceListItem,
745+ }) satisfies ExecutorIntegrationListItem,
746746 )
747747 .sort((left, right) => left.name.localeCompare(right.name) || left.id.localeCompare(right.id));
748748
749749 const page = paginate(sortedWithCounts, offset, limit);
750750
751751 yield* Effect.annotateCurrentSpan({
752− "executor.sources.candidate_count": integrations.length,
753− "executor.sources.match_count": sortedWithCounts.length,
754− "executor.sources.result_count": page.items.length,
755− "executor.sources.has_more": page.hasMore,
752+ "executor.integrations.candidate_count": integrations.length,
753+ "executor.integrations.match_count": sortedWithCounts.length,
754+ "executor.integrations.result_count": page.items.length,
755+ "executor.integrations.has_more": page.hasMore,
756756 });
757757 return page;
758758 });
packages/core/sdk/README.md+3−3
@@ -1,6 +1,6 @@
11 # @executor-js/sdk
22
3−A TypeScript SDK for building executors that wire together tool sources, secrets, and policies across MCP, OpenAPI, GraphQL, and custom plugins.
3+A TypeScript SDK for building executors that wire together integrations, secrets, and policies across MCP, OpenAPI, GraphQL, and custom plugins.
44
55 The default surface is `Promise`-based — plugins are built on [Effect](https://effect.website/) under the hood, but consumers never have to touch it.
66
@@ -132,7 +132,7 @@ The same pattern is what every shipped `@executor-js/plugin-*` package does inte · 125 unmodified lines
132132
133133 These plugin packages are published from the monorepo:
134134
135−- [`@executor-js/plugin-mcp`](https://www.npmjs.com/package/@executor-js/plugin-mcp) — Model Context Protocol sources (stdio + remote)
135+- [`@executor-js/plugin-mcp`](https://www.npmjs.com/package/@executor-js/plugin-mcp) — Model Context Protocol integrations (stdio + remote)
136136 - [`@executor-js/plugin-openapi`](https://www.npmjs.com/package/@executor-js/plugin-openapi) — OpenAPI specs and Google Discovery documents as tools
137137 - [`@executor-js/plugin-graphql`](https://www.npmjs.com/package/@executor-js/plugin-graphql) — GraphQL endpoints as tools
138138 - [`@executor-js/plugin-file-secrets`](https://www.npmjs.com/package/@executor-js/plugin-file-secrets) — file-backed secret store
@@ -161,7 +161,7 @@ const value = await executor.secrets.get("github-token"); · 22 unmodified lines
161161 const refs = await executor.secrets.list();
162162 ```
163163
164−Plugins that need a token — HTTP-backed sources, OAuth flows, etc. — accept a secret id at the source-config layer and resolve through the executor, so token strings never live in your config files.
164+Plugins that need a token — HTTP-backed integrations, OAuth flows, etc. — accept a secret id at the integration-config layer and resolve through the executor, so token strings never live in your config files.
165165
166166 ## Status
167167
packages/core/sdk/src/auth-tool-failure.ts+7−5
@@ -10,7 +10,7 @@ export type AuthToolFailureCode = · 9 unmodified lines
1010 export type AuthToolFailureInput = {
1111 readonly code: AuthToolFailureCode;
1212 readonly message: string;
13− readonly source?: {
13+ readonly integration?: {
1414 readonly id: string;
1515 readonly scope?: string;
1616 };
@@ -27,12 +27,12 @@ export type AuthToolFailureInput = { · 10 unmodified lines
2727 readonly details?: unknown;
2828 };
2929 readonly recovery?: {
30− readonly configureSourceTool?: string;
30+ readonly configureIntegrationTool?: string;
3131 };
3232 };
3333
3434 // In v1.5 a connection IS the credential: there is no standalone secret to
35−// "bind" to a source afterward. Manually-entered credentials are created via
35+// "bind" to an integration afterward. Manually-entered credentials are created via
3636 // the connection handoff (the user enters the value in the web UI, which
3737 // creates the bound connection in one step); OAuth credentials are minted by
3838 // the OAuth start flow. These strings are read by the agent resolving the
@@ -41,7 +41,9 @@ const authRecovery = (input?: AuthToolFailureInput["recovery"]) => ({ · 2 unmodified lines
4141 createConnectionTool: "executor.coreTools.connections.createHandoff",
4242 startOAuthTool: "executor.coreTools.oauth.start",
4343 listConnectionsTool: "executor.coreTools.connections.list",
44− ...(input?.configureSourceTool ? { configureSourceTool: input.configureSourceTool } : {}),
44+ ...(input?.configureIntegrationTool
45+ ? { configureIntegrationTool: input.configureIntegrationTool }
46+ : {}),
4547 connectionInstructions:
4648 "For API keys and tokens, call createConnectionTool for the integration to get a browser URL; the user enters the credential there, which creates the bound connection. Do not ask the user to paste secrets into chat. Then call listConnectionsTool to confirm the connection exists before retrying this tool.",
4749 oauthInstructions:
@@ -56,7 +58,7 @@ export const authToolFailure = <T = never>(input: AuthToolFailureInput): ToolRes · 8 unmodified lines
5658 ...(input.status !== undefined ? { status: input.status } : {}),
5759 details: {
5860 category: "authentication",
59− ...(input.source ? { source: input.source } : {}),
61+ ...(input.integration ? { integration: input.integration } : {}),
6062 ...(input.credential ? { credential: input.credential } : {}),
6163 ...(input.upstream ? { upstream: input.upstream } : {}),
6264 recovery: authRecovery(input.recovery),
packages/core/sdk/src/blob.ts+1−1
@@ -1,6 +1,6 @@
11 // ---------------------------------------------------------------------------
22 // BlobStore — the seam for large, opaque, write-once data. Blobs are stored
3−// in FumaDB with their own lifecycle and namespacing, separate from source
3+// in FumaDB with their own lifecycle and namespacing, separate from integration
44 // metadata and plugin-owned config rows.
55 //
66 // Plugins see a `PluginBlobStore` that's already namespaced to the
packages/core/sdk/src/client.test.ts+2−2
@@ -68,13 +68,13 @@ describe("createPluginAtomClient", () => { · 67 unmodified lines
6868 });
6969
7070 it("can apply dynamic server connection URL and auth to plugin requests", () => {
71− const request = HttpClientRequest.get("/graphql/sources");
71+ const request = HttpClientRequest.get("/graphql/integrations");
7272 const transformed = applyPluginAtomClientRequestTransform(request, {
7373 baseUrl: () => "https://executor.example/api",
7474 authorizationHeader: () => "Bearer key_123",
7575 });
7676
77− expect(transformed.url).toBe("https://executor.example/api/graphql/sources");
77+ expect(transformed.url).toBe("https://executor.example/api/graphql/integrations");
7878 expect(transformed.headers.authorization).toBe("Bearer key_123");
7979 });
8080
packages/core/sdk/src/client.ts+5−5
@@ -177,7 +177,7 @@ export type EditSheetApplyResult = · 176 unmodified lines
177177 | { readonly ok: false };
178178
179179 export interface EditSheetSectionProps {
180− readonly sourceId: string;
180+ readonly integrationId: string;
181181 readonly onPendingChange?: (apply: (() => Promise<EditSheetApplyResult>) | null) => void;
182182 }
183183
@@ -199,7 +199,7 @@ export interface IntegrationPlugin { · 15 unmodified lines
199199 /** Legacy full-page edit surface. No host renders this anymore — plugin
200200 * configuration lives in the integration Edit sheet via `editSheet`. */
201201 readonly edit?: ComponentType<{
202− readonly sourceId: string;
202+ readonly integrationId: string;
203203 readonly onSave: () => void;
204204 }>;
205205 /** Plugin-owned configuration rendered inside the integration's Edit sheet,
@@ -210,7 +210,7 @@ export interface IntegrationPlugin { · 4 unmodified lines
210210 * apply keeps the sheet open with the section showing its own error. */
211211 readonly editSheet?: ComponentType<EditSheetSectionProps>;
212212 readonly summary?: ComponentType<{
213− readonly sourceId: string;
213+ readonly integrationId: string;
214214 readonly variant?: "badge" | "panel";
215215 readonly onAction?: () => void;
216216 }>;
@@ -218,7 +218,7 @@ export interface IntegrationPlugin { · 1 unmodified line
218218 * the detail page's Accounts tab. Plugins that declare auth methods implement
219219 * this; the page falls back to a generic accounts list when absent. */
220220 readonly accounts?: ComponentType<{
221− readonly sourceId: string;
221+ readonly integrationId: string;
222222 readonly integrationName: string;
223223 readonly accountHandoff?: IntegrationAccountHandoff | null;
224224 }>;
@@ -248,7 +248,7 @@ export interface ClientPluginSpec<TId extends string = string> { · 23 unmodified lines
248248 readonly widgets?: readonly WidgetDecl[];
249249 readonly slots?: Record<string, SlotComponent>;
250250 /** Integration plugin contribution — populated by plugins that expose
251− * `kind` rows in the core `source` table (openapi, mcp, graphql).
251+ * `kind` rows in the core `integration` table (openapi, mcp, graphql).
252252 * The host's integrations page derives its provider
253253 * list from the union of every loaded plugin's `integrationPlugin`. */
254254 readonly integrationPlugin?: IntegrationPlugin;
packages/core/sdk/src/core-tools.ts+1−1
@@ -514,7 +514,7 @@ export const coreToolsPlugin = definePlugin((options: CoreToolsPluginOptions = { · 513 unmodified lines
514514 storage: () => ({}),
515515 extension: () => ({}),
516516
517− staticSources: () => [
517+ staticIntegrations: () => [
518518 {
519519 id: "coreTools",
520520 kind: "executor",
packages/core/sdk/src/executor.ts+38−36
@@ -118,7 +118,7 @@ import type { · 117 unmodified lines
118118 PluginCtx,
119119 PluginExtensions,
120120 ResolveToolsResult,
121− StaticSourceDecl,
121+ StaticIntegrationDecl,
122122 StaticToolDecl,
123123 StorageDeps,
124124 ToolPolicyProvider,
@@ -1241,7 +1241,7 @@ const approvalArgumentPreview = (args: unknown): string => { · 1116 unmodified lines
12411241 // ---------------------------------------------------------------------------
12421242
12431243 interface StaticTools {
1244− readonly source: StaticSourceDecl;
1244+ readonly integration: StaticIntegrationDecl;
12451245 readonly tool: StaticToolDecl;
12461246 readonly pluginId: string;
12471247 readonly ctx: PluginCtx<unknown>;
@@ -1253,9 +1253,9 @@ interface PluginRuntime { · 5 unmodified lines
12531253 readonly ctx: PluginCtx<unknown>;
12541254 }
12551255
1256−const EXECUTOR_SOURCE_ID = "executor";
1257−const EXECUTOR_SOURCE: StaticSourceDecl = {
1258− id: EXECUTOR_SOURCE_ID,
1256+const EXECUTOR_INTEGRATION_ID = "executor";
1257+const EXECUTOR_INTEGRATION: StaticIntegrationDecl = {
1258+ id: EXECUTOR_INTEGRATION_ID,
12591259 kind: "built-in",
12601260 name: "Executor",
12611261 canRemove: false,
@@ -1374,32 +1374,32 @@ export const createExecutor = <const TPlugins extends readonly AnyPlugin[] = rea · 112 unmodified lines
13741374 const credentialProviderOrder: string[] = [];
13751375
13761376 const staticToolOwner = (): Owner => (subject == null ? "org" : "user");
1377− const staticToolConnection = (source: StaticSourceDecl): ConnectionName =>
1378− ConnectionName.make(source.id === EXECUTOR_SOURCE_ID ? "coreTools" : "static");
1377+ const staticToolConnection = (integration: StaticIntegrationDecl): ConnectionName =>
1378+ ConnectionName.make(integration.id === EXECUTOR_INTEGRATION_ID ? "coreTools" : "static");
13791379
1380− const staticSources = (): readonly StaticSourceDecl[] => {
1381− const byId = new Map<string, StaticSourceDecl>();
1380+ const staticIntegrations = (): readonly StaticIntegrationDecl[] => {
1381+ const byId = new Map<string, StaticIntegrationDecl>();
13821382 for (const entry of staticTools.values()) {
1383− if (!byId.has(entry.source.id)) byId.set(entry.source.id, entry.source);
1383+ if (!byId.has(entry.integration.id)) byId.set(entry.integration.id, entry.integration);
13841384 }
13851385 return [...byId.values()];
13861386 };
13871387
1388− const staticSourceToIntegration = (source: StaticSourceDecl): Integration => ({
1389− slug: IntegrationSlug.make(source.id),
1390− name: source.name,
1391− description: source.name,
1392− kind: source.kind,
1393− canRemove: source.canRemove ?? false,
1394− canRefresh: source.canRefresh ?? false,
1388+ const staticDeclToIntegration = (integration: StaticIntegrationDecl): Integration => ({
1389+ slug: IntegrationSlug.make(integration.id),
1390+ name: integration.name,
1391+ description: integration.name,
1392+ kind: integration.kind,
1393+ canRemove: integration.canRemove ?? false,
1394+ canRefresh: integration.canRefresh ?? false,
13951395 authMethods: [],
13961396 });
13971397
13981398 const staticToolToTool = (entry: StaticTools): Tool => ({
1399− address: ToolAddress.make(`${entry.source.id}.${entry.tool.name}`),
1399+ address: ToolAddress.make(`${entry.integration.id}.${entry.tool.name}`),
14001400 owner: staticToolOwner(),
1401− integration: IntegrationSlug.make(entry.source.id),
1402− connection: staticToolConnection(entry.source),
1401+ integration: IntegrationSlug.make(entry.integration.id),
1402+ connection: staticToolConnection(entry.integration),
14031403 name: ToolName.make(entry.tool.name),
14041404 pluginId: entry.pluginId,
14051405 description: entry.tool.description,
@@ -1814,21 +1814,21 @@ export const createExecutor = <const TPlugins extends readonly AnyPlugin[] = rea · 408 unmodified lines
18141814 const integrationsList = (): Effect.Effect<readonly Integration[], StorageFailure> =>
18151815 Effect.gen(function* () {
18161816 const rows = yield* core.findMany("integration", {});
1817− const staticIntegrations = staticSources().map(staticSourceToIntegration);
1817+ const staticIntegrationList = staticIntegrations().map(staticDeclToIntegration);
18181818 const dbIntegrations = rows.map((row) =>
18191819 rowToIntegration(row, describeAuthMethodsForRow(row), describeDisplayForRow(row)),
18201820 );
18211821 // A scoped toolkit must not advertise providers it grants no tools from
1822− // (mirrors `connectionsList`). Static sources are system namespaces, not
1822+ // (mirrors `connectionsList`). Static integrations are system namespaces, not
18231823 // user providers, so they stay; DB-backed integrations are filtered to
18241824 // those that contribute at least one visible tool under the active policy.
1825− if (!activeToolPolicyProvider) return [...staticIntegrations, ...dbIntegrations];
1825+ if (!activeToolPolicyProvider) return [...staticIntegrationList, ...dbIntegrations];
18261826 const visibleTools = yield* toolsList({ includeAnnotations: false });
18271827 const visibleIntegrationSlugs = new Set(
18281828 visibleTools.filter((tool) => !tool.static).map((tool) => String(tool.integration)),
18291829 );
18301830 return [
1831− ...staticIntegrations,
1831+ ...staticIntegrationList,
18321832 ...dbIntegrations.filter((integration) =>
18331833 visibleIntegrationSlugs.has(String(integration.slug)),
18341834 ),
@@ -1839,8 +1839,10 @@ export const createExecutor = <const TPlugins extends readonly AnyPlugin[] = rea · 4 unmodified lines
18391839 slug: IntegrationSlug,
18401840 ): Effect.Effect<Integration | null, StorageFailure> =>
18411841 Effect.gen(function* () {
1842− const staticSource = staticSources().find((source) => source.id === String(slug));
1843− if (staticSource) return staticSourceToIntegration(staticSource);
1842+ const staticIntegration = staticIntegrations().find(
1843+ (integration) => integration.id === String(slug),
1844+ );
1845+ if (staticIntegration) return staticDeclToIntegration(staticIntegration);
18441846 const row = yield* findIntegrationRow(slug);
18451847 return row
18461848 ? rowToIntegration(row, describeAuthMethodsForRow(row), describeDisplayForRow(row))
@@ -2135,7 +2137,7 @@ export const createExecutor = <const TPlugins extends readonly AnyPlugin[] = rea · 288 unmodified lines
21352137 );
21362138
21372139 if (result.incomplete === true) {
2138− // Non-authoritative listing (source unreachable, auth not ready).
2140+ // Non-authoritative listing (integration unreachable, auth not ready).
21392141 // Keep the existing catalog — replacing it would wipe working tools
21402142 // over a transient outage — and stamp the sync time anyway so a down
21412143 // server isn't re-dialed on every read; the freshness TTL re-attempts
@@ -3871,21 +3873,21 @@ export const createExecutor = <const TPlugins extends readonly AnyPlugin[] = rea · 1729 unmodified lines
38713873 }
38723874 }
38733875
3874− // Build extension FIRST so it's available as `self` for staticSources.
3876+ // Build extension FIRST so it's available as `self` for staticIntegrations.
38753877 const extension: object = plugin.extension ? plugin.extension(ctx) : {};
38763878 if (plugin.extension) {
38773879 extensions[plugin.id] = extension;
38783880 }
38793881
3880− const decls = plugin.staticSources ? plugin.staticSources(extension) : [];
3881− for (const source of decls) {
3882− const mountUnderExecutor = source.kind === "executor";
3883− const mountedSource = mountUnderExecutor ? EXECUTOR_SOURCE : source;
3884− for (const tool of source.tools) {
3882+ const decls = plugin.staticIntegrations ? plugin.staticIntegrations(extension) : [];
3883+ for (const integration of decls) {
3884+ const mountUnderExecutor = integration.kind === "executor";
3885+ const mountedIntegration = mountUnderExecutor ? EXECUTOR_INTEGRATION : integration;
3886+ for (const tool of integration.tools) {
38853887 const mountedTool = mountUnderExecutor
3886− ? { ...tool, name: `${source.id}.${tool.name}` }
3888+ ? { ...tool, name: `${integration.id}.${tool.name}` }
38873889 : tool;
3888− const fqid = `${mountedSource.id}.${mountedTool.name}`;
3890+ const fqid = `${mountedIntegration.id}.${mountedTool.name}`;
38893891 if (staticTools.has(fqid)) {
38903892 return yield* new StorageError({
38913893 message: `Duplicate static tool id: ${fqid} (plugin ${plugin.id})`,
@@ -3893,7 +3895,7 @@ export const createExecutor = <const TPlugins extends readonly AnyPlugin[] = rea · 1 unmodified line
38933895 });
38943896 }
38953897 staticTools.set(fqid, {
3896− source: mountedSource,
3898+ integration: mountedIntegration,
38973899 tool: mountedTool,
38983900 pluginId: plugin.id,
38993901 ctx,
packages/core/sdk/src/index.ts+1−1
@@ -303,7 +303,7 @@ export { · 302 unmodified lines
303303 type ToolPolicyProvider,
304304 type ToolPolicyProviderRule,
305305 type IntegrationRecord,
306− type StaticSourceDecl,
306+ type StaticIntegrationDecl,
307307 type StaticToolDecl,
308308 type StaticToolSchema,
309309 type StaticToolExecuteContext,
packages/core/sdk/src/migration-spec.test.ts+2−2
@@ -484,13 +484,13 @@ describe("migrateOpenApiSourceConfig", () => { · 483 unmodified lines
484484 });
485485 const config = r.config as {
486486 readonly spec?: string;
487− readonly sourceUrl?: string;
487+ readonly specUrl?: string;
488488 readonly baseUrl?: string;
489489 readonly headers?: Record<string, string>;
490490 readonly authenticationTemplate?: unknown;
491491 };
492492 expect(config.spec).toBe("{openapi}");
493− expect(config.sourceUrl).toBe("https://api.example.com/openapi.json");
493+ expect(config.specUrl).toBe("https://api.example.com/openapi.json");
494494 expect(config.baseUrl).toBe("https://api.example.com");
495495 // The literal header is static config; the credential header became a template.
496496 expect(config.headers).toEqual({ "User-Agent": "executor/1.0" });
packages/core/sdk/src/migration-spec.ts+2−2
@@ -608,7 +608,7 @@ export interface V1OpenApiSourceConfig extends V1OpenApiAuthConfig { · 607 unmodified lines
608608
609609 export interface V2OpenApiIntegrationConfig {
610610 readonly spec?: string;
611− readonly sourceUrl?: string;
611+ readonly specUrl?: string;
612612 readonly baseUrl?: string;
613613 readonly googleDiscoveryUrls?: readonly string[];
614614 readonly headers?: Record<string, string>;
@@ -630,7 +630,7 @@ export const migrateOpenApiSourceConfig = (v1: V1OpenApiSourceConfig): MigratedS · 15 unmodified lines
630630 const auth = migrateOpenApiAuthTemplate(v1);
631631 const config: V2OpenApiIntegrationConfig = {
632632 ...(v1.spec !== undefined ? { spec: v1.spec } : {}),
633− ...(v1.sourceUrl !== undefined ? { sourceUrl: v1.sourceUrl } : {}),
633+ ...(v1.sourceUrl !== undefined ? { specUrl: v1.sourceUrl } : {}),
634634 ...(v1.baseUrl !== undefined ? { baseUrl: v1.baseUrl } : {}),
635635 ...(v1.googleDiscoveryUrls !== undefined
636636 ? { googleDiscoveryUrls: v1.googleDiscoveryUrls }
packages/core/sdk/src/plugin.ts+13−11
@@ -145,9 +145,9 @@ export interface IntegrationRecord extends Integration { · 144 unmodified lines
145145
146146 // ---------------------------------------------------------------------------
147147 // PluginCtx — threaded into every extension method, static tool handler, and
148−// dynamic tool handler. The v2 fold: `core.sources` → `core.integrations`,
149−// `secrets`/`connections`/`credentialBindings` → `connections` (provider-
150−// resolved) + `providers`, `scopes` → `owner`.
148+// dynamic tool handler. The v2 fold folded `core.sources` into
149+// `core.integrations`, `secrets`/`connections`/`credentialBindings` into
150+// `connections` (provider-resolved) + `providers`, and `scopes` into `owner`.
151151 // ---------------------------------------------------------------------------
152152
153153 export interface PluginCtx<TStore = unknown> {
@@ -290,11 +290,11 @@ export interface ResolveToolsResult { · 136 unmodified lines
290290 readonly tools: readonly ToolDef[];
291291 /** Shared JSON-schema `$defs` reachable from the tools' `$ref`s. */
292292 readonly definitions?: Record<string, unknown>;
293− /** The source could not be (fully) enumerated: unreachable server, auth not
293+ /** The integration could not be (fully) enumerated: unreachable server, auth not
294294 * ready, listing aborted. The result is non-authoritative, so the executor
295295 * keeps the connection's existing persisted catalog instead of replacing it
296296 * (a transient outage must not wipe working tools). Omit / `false` when the
297− * listing is authoritative, including a genuine "this source has zero
297+ * listing is authoritative, including a genuine "this integration has zero
298298 * tools". */
299299 readonly incomplete?: boolean;
300300 /** Human-readable reason for an incomplete listing. Persisted by core when it
@@ -354,7 +354,7 @@ export interface HealthCheckCandidatesInput<TStore = unknown> { · 53 unmodified lines
354354 }
355355
356356 // ---------------------------------------------------------------------------
357−// Static tool / source declarations. Unchanged from v1 except the ctx shape.
357+// Static tool / integration declarations. Unchanged from v1 except the ctx shape.
358358 // ---------------------------------------------------------------------------
359359
360360 export interface StaticToolHandlerInput<TStore = unknown> {
@@ -433,7 +433,7 @@ export const tool = < · 72 unmodified lines
433433 ),
434434 });
435435
436−export interface StaticSourceDecl<TStore = unknown> {
436+export interface StaticIntegrationDecl<TStore = unknown> {
437437 readonly id: string;
438438 readonly kind: string;
439439 readonly name: string;
@@ -574,12 +574,14 @@ export interface PluginSpec< · 134 unmodified lines
574574 readonly integrationPresets?: readonly IntegrationPreset[];
575575
576576 /** Build the plugin's extension API — becomes `executor[plugin.id]` and the
577− * `self` passed to `staticSources`. Field order matters: `extension` MUST
578− * appear before `staticSources`. */
577+ * `self` passed to `staticIntegrations`. Field order matters: `extension` MUST
578+ * appear before `staticIntegrations`. */
579579 readonly extension?: (ctx: PluginCtx<TStore>) => TExtension;
580580
581− /** Static sources contributed by this plugin with inline tool handlers. */
582− readonly staticSources?: (self: NoInfer<TExtension>) => readonly StaticSourceDecl<TStore>[];
581+ /** Static integrations contributed by this plugin with inline tool handlers. */
582+ readonly staticIntegrations?: (
583+ self: NoInfer<TExtension>,
584+ ) => readonly StaticIntegrationDecl<TStore>[];
583585
584586 /** HttpApiGroup contributed by this plugin. */
585587 readonly routes?: () => TGroup;
packages/core/sdk/src/policies.test.ts+1−1
@@ -516,7 +516,7 @@ describe("active tool-policy provider", () => { · 515 unmodified lines
516516 const staticPlugin = definePlugin(() => ({
517517 id: "toolkit-fixture" as const,
518518 storage: () => ({}),
519− staticSources: () => [
519+ staticIntegrations: () => [
520520 {
521521 id: "toolkit-fixture.ctl",
522522 kind: "control" as const,
packages/core/sdk/src/promise-executor.ts+1−1
@@ -8,7 +8,7 @@ · 7 unmodified lines
88 // that — the proxy flattens plugin extension methods too.
99 //
1010 // Not a goal: authoring plugins in Promise style. The plugin model
11−// (storage, schema, staticSources, Effect ctx) is Effect-only. Bring
11+// (storage, schema, staticIntegrations, Effect ctx) is Effect-only. Bring
1212 // your own `@executor-js/plugin-*` from the Effect side.
1313 // ---------------------------------------------------------------------------
1414
packages/core/sdk/src/promise.test.ts+2−2
@@ -15,7 +15,7 @@ import { Effect, Schema } from "effect"; · 14 unmodified lines
1515 const echoPlugin = definePlugin(() => ({
1616 id: "echo" as const,
1717 storage: () => ({}),
18− staticSources: () => [
18+ staticIntegrations: () => [
1919 {
2020 id: "echo.ctl",
2121 kind: "control" as const,
@@ -72,7 +72,7 @@ describe("promise/createExecutor", () => { · 50 unmodified lines
7272 const approvedPlugin = definePlugin(() => ({
7373 id: "ap" as const,
7474 storage: () => ({}),
75− staticSources: () => [
75+ staticIntegrations: () => [
7676 {
7777 id: "ap.ctl",
7878 kind: "control" as const,
packages/core/sdk/src/sqlite-config-blob-migration.test.ts+2−2
@@ -49,7 +49,7 @@ describe("runSqliteConfigBlobMigration", () => { · 48 unmodified lines
4949 tenant: "t1",
5050 slug: "legacy_api",
5151 pluginId: "openapi",
52− config: { spec, sourceUrl: "https://example.com/spec.json" },
52+ config: { spec, specUrl: "https://example.com/spec.json" },
5353 }),
5454 );
5555
@@ -63,7 +63,7 @@ describe("runSqliteConfigBlobMigration", () => { · 7 unmodified lines
6363 expect(config.spec).toBeUndefined();
6464 expect(typeof config.specHash).toBe("string");
6565 // Untouched fields survive the rewrite.
66− expect(config.sourceUrl).toBe("https://example.com/spec.json");
66+ expect(config.specUrl).toBe("https://example.com/spec.json");
6767
6868 // The blob row uses the runtime's exact naming: namespace
6969 // `o:<tenant>/<pluginId>`, key `spec/<hash>`, id JSON.stringify pair.
packages/hosts/mcp/src/tool-server.test.ts+1−1
@@ -1095,7 +1095,7 @@ describe("MCP host server — client without elicitation (pause/resume)", () => · 1094 unmodified lines
10951095 Effect.succeed(
10961096 makePausedResult(
10971097 "exec_confirm",
1098− FormElicitation.make({ message: "Confirm source add", requestedSchema: {} }),
1098+ FormElicitation.make({ message: "Confirm integration add", requestedSchema: {} }),
10991099 ),
11001100 ),
11011101 });
packages/kernel/core/src/strip-types.test.ts+1−1
@@ -60,7 +60,7 @@ describe("stripTypeScript", () => { · 59 unmodified lines
6060 // a function expression.
6161 const code = `
6262 const fetchResources = async (sourceId: string): Promise<Array<{ id: string }>> => {
63− const result = await tools.executor.sources.list();
63+ const result = await tools.executor.integrations.list();
6464 return result.items;
6565 };
6666 return fetchResources('example-source');
packages/kernel/ir/src/registry.ts+2−2
@@ -4,7 +4,7 @@ import type { JsonSchema } from "effect/JsonSchema"; · 3 unmodified lines
44 export const ToolRegistration = Schema.Struct({
55 path: Schema.String,
66 description: Schema.optional(Schema.String),
7− sourceId: Schema.String,
7+ integrationId: Schema.String,
88 input: Schema.optional(Schema.String),
99 output: Schema.optional(Schema.String),
1010 error: Schema.optional(Schema.String),
@@ -21,7 +21,7 @@ export type SerializedCatalog = typeof SerializedCatalog.Type; · 10 unmodified lines
2121 export interface LiveToolRegistration {
2222 readonly path: string;
2323 readonly description?: string;
24− readonly sourceId: string;
24+ readonly integrationId: string;
2525 readonly input?: Schema.Top;
2626 readonly output?: Schema.Top;
2727 readonly error?: Schema.Top;
packages/kernel/ir/src/serialize.ts+2−2
@@ -29,7 +29,7 @@ export function serialize(catalog: LiveCatalog): typeof SerializedCatalog.Type { · 28 unmodified lines
2929 const tools = catalog.tools.map((tool) => ({
3030 path: tool.path,
3131 description: tool.description,
32− sourceId: tool.sourceId,
32+ integrationId: tool.integrationId,
3333 input: schemaToRef(tool.input),
3434 output: schemaToRef(tool.output),
3535 error: schemaToRef(tool.error),
@@ -70,7 +70,7 @@ export function deserializeToJsonSchema(serialized: typeof SerializedCatalog.Typ · 34 unmodified lines
7070 tools: serialized.tools.map((tool) => ({
7171 path: tool.path,
7272 description: tool.description,
73− sourceId: tool.sourceId,
73+ integrationId: tool.integrationId,
7474 input: resolveRef(tool.input),
7575 output: resolveRef(tool.output),
7676 error: resolveRef(tool.error),
packages/plugins/desktop-settings/src/client.tsx+5−5
@@ -198,8 +198,8 @@ function SettingsPage() { · 197 unmodified lines
198198 const authLabel = "Bearer token";
199199 const cliProfileCommand = `executor server add desktop ${connection.origin} --default`;
200200 const cliUseCommand = authToken
201− ? `EXECUTOR_AUTH_TOKEN=${authToken} executor tools sources --server desktop`
202− : "executor tools sources --server desktop";
201+ ? `EXECUTOR_AUTH_TOKEN=${authToken} executor tools integrations --server desktop`
202+ : "executor tools integrations --server desktop";
203203
204204 return (
205205 <div style={pageFrameStyle}>
@@ -360,8 +360,8 @@ function SettingsPage() { · 154 unmodified lines
360360 <div style={{ fontSize: "0.875rem", fontWeight: 600 }}>Diagnostics</div>
361361 <span style={{ fontSize: "0.75rem", color: "var(--muted-foreground, #888)" }}>
362362 Packs app and server logs, crash dumps, and version info into a zip in your
363− Downloads folder — attach it when reporting a bug. Your sources, secrets, and bearer
364− token are not included.
363+ Downloads folder — attach it when reporting a bug. Your integrations, secrets, and
364+ bearer token are not included.
365365 </span>
366366 <div style={{ display: "flex", gap: "0.5rem", alignItems: "center" }}>
367367 {/* oxlint-disable-next-line react/forbid-elements -- plugin component uses raw HTML controls per SDK convention */}
@@ -430,7 +430,7 @@ export default defineClientPlugin({ · 62 unmodified lines
430430 path: "/",
431431 component: SettingsPage,
432432 // Only contribute a nav entry when running inside the desktop app.
433− // Web users see an empty Sources nav without a non-functional
433+ // Web users see an empty Integrations nav without a non-functional
434434 // "Settings" link.
435435 ...(inDesktop ? { nav: { label: "Settings" } } : {}),
436436 },
packages/plugins/desktop-settings/src/server.ts+1−1
@@ -37,7 +37,7 @@ export const desktopSettingsPlugin = definePlugin((options: DesktopSettingsPlugi · 36 unmodified lines
3737 id: "desktop-settings" as const,
3838 packageName: "@executor-js/plugin-desktop-settings",
3939 storage: () => ({}),
40− staticSources: () => [
40+ staticIntegrations: () => [
4141 {
4242 id: "desktopSettings",
4343 kind: "executor",
packages/plugins/graphql/README.md+2−2
@@ -22,7 +22,7 @@ const executor = await createExecutor({ · 21 unmodified lines
2222 });
2323
2424 // Public endpoint — no auth
25−await executor.graphql.addSource({
25+await executor.graphql.addIntegration({
2626 scope: executor.scopes[0]!.id,
2727 endpoint: "https://graphql.anilist.co",
2828 namespace: "anilist",
@@ -55,7 +55,7 @@ await executor.secrets.set({ · 26 unmodified lines
5555 scope,
5656 });
5757
58−await executor.graphql.addSource({
58+await executor.graphql.addIntegration({
5959 scope,
6060 endpoint: "https://api.github.com/graphql",
6161 namespace: "github",
packages/plugins/graphql/src/react/AddGraphqlIntegration.tsxrenamed from packages/plugins/graphql/src/react/AddGraphqlSource.tsx+6−6
@@ -24,7 +24,7 @@ import { · 23 unmodified lines
2424 } from "@executor-js/react/lib/integration-add";
2525
2626 import { createGraphqlIntegrationOptimistic } from "./atoms";
27−import { GraphqlSourceFields } from "./GraphqlSourceFields";
27+import { GraphqlIntegrationFields } from "./GraphqlIntegrationFields";
2828 import { graphqlAuthMethodInputsFromPlacements } from "./auth-method-config";
2929 import type { GraphqlAuthMethodInput } from "../sdk/types";
3030
@@ -38,7 +38,7 @@ import type { GraphqlAuthMethodInput } from "../sdk/types"; · 7 unmodified lines
3838 // — a fresh [] every render would re-seed the list each render).
3939 const NO_SEEDS: readonly AuthMethodSeed[] = [];
4040
41−export default function AddGraphqlSource(props: {
41+export default function AddGraphqlIntegration(props: {
4242 onComplete: (slug?: string) => void;
4343 onCancel: () => void;
4444 initialUrl?: string;
@@ -93,7 +93,7 @@ export default function AddGraphqlSource(props: { · 48 unmodified lines
9393
9494 const canAdd = endpoint.trim().length > 0 && apiKeyComplete && !adding && !slugAlreadyExists;
9595
96− const sourceIdentity = useCallback(() => {
96+ const integrationIdentity = useCallback(() => {
9797 const trimmedEndpoint = endpoint.trim();
9898 const slug = resolvedSlug;
9999 const displayName =
@@ -104,7 +104,7 @@ export default function AddGraphqlSource(props: { · 4 unmodified lines
104104 const handleAdd = async (): Promise<void> => {
105105 setAdding(true);
106106 setAddError(null);
107− const { trimmedEndpoint, slug, displayName } = sourceIdentity();
107+ const { trimmedEndpoint, slug, displayName } = integrationIdentity();
108108
109109 const integrationExit = await doAddIntegration({
110110 payload: {
@@ -119,7 +119,7 @@ export default function AddGraphqlSource(props: { · 8 unmodified lines
119119 reactivityKeys: integrationWriteKeys,
120120 });
121121 if (Exit.isFailure(integrationExit)) {
122− setAddError(addIntegrationErrorMessage(integrationExit, slug, "Failed to add source"));
122+ setAddError(addIntegrationErrorMessage(integrationExit, slug, "Failed to add integration"));
123123 setAdding(false);
124124 return;
125125 }
@@ -132,7 +132,7 @@ export default function AddGraphqlSource(props: { · 6 unmodified lines
132132 <div className="flex flex-1 flex-col gap-6">
133133 <h1 className="text-xl font-semibold text-foreground">Add GraphQL integration</h1>
134134
135− <GraphqlSourceFields
135+ <GraphqlIntegrationFields
136136 endpoint={endpoint}
137137 onEndpointChange={setEndpoint}
138138 identity={identity}
packages/plugins/graphql/src/react/GraphqlAccountsPanel.tsx+3−3
@@ -36,12 +36,12 @@ import type { GraphqlAuthMethod } from "../sdk/types"; · 35 unmodified lines
3636 // ---------------------------------------------------------------------------
3737
3838 export default function GraphqlAccountsPanel(props: {
39− readonly sourceId: string;
39+ readonly integrationId: string;
4040 readonly integrationName: string;
4141 readonly accountHandoff?: IntegrationAccountHandoff | null;
4242 }) {
43− const { sourceId, integrationName, accountHandoff } = props;
44− const slug = IntegrationSlug.make(sourceId);
43+ const { integrationId, integrationName, accountHandoff } = props;
44+ const slug = IntegrationSlug.make(integrationId);
4545 const configResult = useAtomValue(graphqlConfigAtom(slug));
4646 const doConfigure = useAtomSet(graphqlConfigure, { mode: "promiseExit" });
4747
packages/plugins/graphql/src/react/GraphqlIntegrationFields.tsxrenamed from packages/plugins/graphql/src/react/GraphqlSourceFields.tsx+1−1
@@ -10,7 +10,7 @@ import { · 9 unmodified lines
1010 type IntegrationIdentity,
1111 } from "@executor-js/react/plugins/integration-identity";
1212
13−export function GraphqlSourceFields(props: {
13+export function GraphqlIntegrationFields(props: {
1414 readonly endpoint: string;
1515 readonly onEndpointChange: (endpoint: string) => void;
1616 readonly identity: IntegrationIdentity;
packages/plugins/graphql/src/react/index.ts+1−1
@@ -1,3 +1,3 @@
1−export { graphqlIntegrationPlugin } from "./source-plugin";
1+export { graphqlIntegrationPlugin } from "./integration-plugin";
22 export { GraphqlClient } from "./client";
33 export { createGraphqlIntegration, graphqlIntegrationConfigAtom } from "./atoms";
packages/plugins/graphql/src/react/integration-plugin.tsrenamed from packages/plugins/graphql/src/react/source-plugin.ts+1−1
@@ -2,7 +2,7 @@ import { lazy } from "react"; · 1 unmodified line
22 import type { IntegrationPlugin } from "@executor-js/sdk/client";
33 import { graphqlPresets } from "../sdk/presets";
44
5−const importAdd = () => import("./AddGraphqlSource");
5+const importAdd = () => import("./AddGraphqlIntegration");
66 const importAccounts = () => import("./GraphqlAccountsPanel");
77
88 // No `editSheet`: GraphQL has no plugin-specific configuration beyond auth
packages/plugins/graphql/src/react/plugin-client.tsx+1−1
@@ -1,6 +1,6 @@
11 import { defineClientPlugin } from "@executor-js/sdk/client";
22
3−import { graphqlIntegrationPlugin } from "./source-plugin";
3+import { graphqlIntegrationPlugin } from "./integration-plugin";
44
55 export default defineClientPlugin({
66 id: "graphql" as const,
packages/plugins/graphql/src/sdk/plugin.ts+4−4
@@ -166,7 +166,7 @@ const graphqlAuthToolFailure = (failure: GraphqlAuthRequiredError) => · 165 unmodified lines
166166 authToolFailure({
167167 code: failure.code,
168168 message: failure.message,
169− source: { id: failure.integration, scope: failure.owner },
169+ integration: { id: failure.integration, scope: failure.owner },
170170 credential: {
171171 kind: failure.credentialKind,
172172 ...(failure.credentialLabel ? { label: failure.credentialLabel } : {}),
@@ -641,7 +641,7 @@ const makeGraphqlExtension = (ctx: PluginCtx<GraphqlStore>) => { · 468 unmodified lines
641641 : [],
642642 });
643643
644− /** Register the integration in the catalog. Registering a source is a
644+ /** Register the integration in the catalog. Registering an integration is a
645645 * catalog statement ("we use this GraphQL endpoint now") and MUST NOT make a
646646 * network call or require auth — exactly like MCP defers discovery. Live
647647 * introspection (and the operation bindings it yields) is deferred to
@@ -899,7 +899,7 @@ export const graphqlPlugin = definePlugin((options?: GraphqlPluginOptions) => { · 251 unmodified lines
899899 describeAuthMethods: describeGraphqlAuthMethods,
900900 describeIntegrationDisplay: describeGraphqlIntegrationDisplay,
901901
902− staticSources: (self: GraphqlPluginExtension) => [
902+ staticIntegrations: (self: GraphqlPluginExtension) => [
903903 {
904904 id: "graphql",
905905 kind: "executor",
@@ -1149,7 +1149,7 @@ export const graphqlPlugin = definePlugin((options?: GraphqlPluginOptions) => { · 243 unmodified lines
11491149 code: "connection_rejected",
11501150 status: result.status,
11511151 message: `Upstream rejected credentials for GraphQL integration "${integration}" with HTTP ${result.status}. Re-authenticate or update the connection before retrying this tool.`,
1152− source: { id: integration, scope: credential.owner },
1152+ integration: { id: integration, scope: credential.owner },
11531153 credential: { kind: "upstream", label: "Upstream authorization" },
11541154 upstream: {
11551155 status: result.status,
packages/plugins/mcp/README.md+4−4
@@ -1,6 +1,6 @@
11 # @executor-js/plugin-mcp
22
3−Register [Model Context Protocol](https://modelcontextprotocol.io) servers as tool sources for an executor. Supports both stdio-launched servers and remote (HTTP) servers, with optional OAuth.
3+Register [Model Context Protocol](https://modelcontextprotocol.io) servers as tool integrations for an executor. Supports both stdio-launched servers and remote (HTTP) servers, with optional OAuth.
44
55 ## Install
66
@@ -18,7 +18,7 @@ import { mcpPlugin } from "@executor-js/plugin-mcp"; · 11 unmodified lines
1818
1919 const executor = await createExecutor({
2020 onElicitation: "accept-all",
21− // Stdio sources spawn a local subprocess and inherit `process.env` —
21+ // Stdio integrations spawn a local subprocess and inherit `process.env` —
2222 // only enable for trusted single-user contexts.
2323 plugins: [mcpPlugin({ dangerouslyAllowStdioMCP: true })] as const,
2424 });
@@ -26,7 +26,7 @@ const executor = await createExecutor({ · 1 unmodified line
2626 const scope = executor.scopes[0]!.id;
2727
2828 // Remote MCP server
29−await executor.mcp.addSource({
29+await executor.mcp.addIntegration({
3030 scope,
3131 transport: "remote",
3232 name: "Context7",
@@ -34,7 +34,7 @@ await executor.mcp.addSource({ · 1 unmodified line
3434 });
3535
3636 // Stdio MCP server (requires `dangerouslyAllowStdioMCP: true` above)
37−await executor.mcp.addSource({
37+await executor.mcp.addIntegration({
3838 scope,
3939 transport: "stdio",
4040 name: "My Server",
packages/plugins/mcp/src/react/AddMcpIntegration.tsxrenamed from packages/plugins/mcp/src/react/AddMcpSource.tsx+3−3
@@ -36,7 +36,7 @@ import { · 35 unmodified lines
3636 import { integrationWriteKeys } from "@executor-js/react/api/reactivity-keys";
3737 import type { McpAuthMethodInput } from "../sdk/types";
3838 import { probeMcpEndpoint, addMcpServer } from "./atoms";
39−import { McpRemoteSourceFields } from "./McpRemoteSourceFields";
39+import { McpRemoteIntegrationFields } from "./McpRemoteIntegrationFields";
4040 import { mcpAuthMethodInputFromEditorValue, mcpWireAuthInput } from "./auth-method-config";
4141 import { mcpPresets, type McpPreset } from "../sdk/presets";
4242
@@ -161,7 +161,7 @@ function reducer(state: State, action: Action): State { · 118 unmodified lines
161161 // Component
162162 // ---------------------------------------------------------------------------
163163
164−export default function AddMcpSource(props: {
164+export default function AddMcpIntegration(props: {
165165 onComplete: (slug?: string) => void;
166166 onCancel: () => void;
167167 initialUrl?: string;
@@ -418,7 +418,7 @@ export default function AddMcpSource(props: { · 250 unmodified lines
418418
419419 {transport === "remote" ? (
420420 <>
421− <McpRemoteSourceFields
421+ <McpRemoteIntegrationFields
422422 url={state.url}
423423 onUrlChange={(url) => dispatch({ type: "set-url", url })}
424424 identity={remoteIdentity}
packages/plugins/mcp/src/react/EditMcpIntegration.tsxrenamed from packages/plugins/mcp/src/react/EditMcpSource.tsx+8−5
@@ -186,8 +186,8 @@ function StdioReadOnly(props: { · 185 unmodified lines
186186 <div className="space-y-1">
187187 <p className="text-sm font-medium text-foreground">Server command</p>
188188 <p className="text-xs text-muted-foreground">
189− Stdio MCP sources cannot be edited. Remove and recreate the source with the updated
190− command.
189+ Stdio MCP integrations cannot be edited. Remove and recreate the integration with the
190+ updated command.
191191 </p>
192192 </div>
193193 <div className="flex items-center gap-3 rounded-md border border-border/60 bg-muted/40 px-3 py-2">
@@ -204,11 +204,14 @@ function StdioReadOnly(props: { · 10 unmodified lines
204204
205205 // ---------------------------------------------------------------------------
206206 // Main component — the mcp plugin's section of the integration Edit sheet.
207−// `sourceId` is the integration slug (v2).
207+// `integrationId` is the integration slug (v2).
208208 // ---------------------------------------------------------------------------
209209
210−export default function EditMcpSource({ sourceId, onPendingChange }: EditSheetSectionProps) {
211− const slug = IntegrationSlug.make(sourceId);
210+export default function EditMcpIntegration({
211+ integrationId,
212+ onPendingChange,
213+}: EditSheetSectionProps) {
214+ const slug = IntegrationSlug.make(integrationId);
212215 const serverResult = useAtomValue(mcpServerAtom(slug));
213216 const server = AsyncResult.isSuccess(serverResult) ? serverResult.value : null;
214217
packages/plugins/mcp/src/react/McpAccountsPanel.tsx+3−3
@@ -36,12 +36,12 @@ import type { McpAuthMethod } from "../sdk/types"; · 35 unmodified lines
3636 // ---------------------------------------------------------------------------
3737
3838 export default function McpAccountsPanel(props: {
39− readonly sourceId: string;
39+ readonly integrationId: string;
4040 readonly integrationName: string;
4141 readonly accountHandoff?: IntegrationAccountHandoff | null;
4242 }) {
43− const { sourceId, integrationName, accountHandoff } = props;
44− const slug = IntegrationSlug.make(sourceId);
43+ const { integrationId, integrationName, accountHandoff } = props;
44+ const slug = IntegrationSlug.make(integrationId);
4545 const serverResult = useAtomValue(mcpServerAtom(slug));
4646 const doConfigureAuth = useAtomSet(configureMcpAuth, { mode: "promiseExit" });
4747
packages/plugins/mcp/src/react/McpRemoteIntegrationFields.tsxrenamed from packages/plugins/mcp/src/react/McpRemoteSourceFields.tsx+3−3
@@ -22,7 +22,7 @@ import { · 21 unmodified lines
2222 type IntegrationIdentity,
2323 } from "@executor-js/react/plugins/integration-identity";
2424
25−export type McpRemoteSourcePreview = {
25+export type McpRemoteIntegrationPreview = {
2626 readonly name: string;
2727 readonly serverName: string | null;
2828 readonly connected: boolean;
@@ -31,7 +31,7 @@ export type McpRemoteSourcePreview = { · 2 unmodified lines
3131 readonly toolCount: number | null;
3232 };
3333
34−export function McpRemoteSourceFields(props: {
34+export function McpRemoteIntegrationFields(props: {
3535 readonly url: string;
3636 readonly onUrlChange: (url: string) => void;
3737 readonly identity: IntegrationIdentity;
@@ -39,7 +39,7 @@ export function McpRemoteSourceFields(props: { · 1 unmodified line
3939 * `instructions` when the probe connected). */
4040 readonly description?: string;
4141 readonly onDescriptionChange?: (value: string) => void;
42− readonly preview: McpRemoteSourcePreview | null;
42+ readonly preview: McpRemoteIntegrationPreview | null;
4343 readonly probing?: boolean;
4444 readonly error?: string | null;
4545 readonly onRetry?: () => void;
packages/plugins/mcp/src/react/McpSignInButton.tsx+2−2
@@ -28,8 +28,8 @@ import type { McpAuthMethod } from "../sdk/types"; · 27 unmodified lines
2828 // a non-org host like local), never read from an ambient owner.
2929 // ---------------------------------------------------------------------------
3030
31−export default function McpSignInButton(props: { sourceId: string; owner?: Owner }) {
32− const slug = IntegrationSlug.make(props.sourceId);
31+export default function McpSignInButton(props: { integrationId: string; owner?: Owner }) {
32+ const slug = IntegrationSlug.make(props.integrationId);
3333 const targetOwner: Owner = props.owner ?? "org";
3434 const serverResult = useAtomValue(mcpServerAtom(slug));
3535 const connectionsResult = useAtomValue(connectionsAllAtom);
packages/plugins/mcp/src/react/index.ts+2−2
@@ -1,5 +1,5 @@
1−export { mcpIntegrationPlugin, createMcpIntegrationPlugin } from "./source-plugin";
2−export type { McpIntegrationPluginOptions } from "./source-plugin";
1+export { mcpIntegrationPlugin, createMcpIntegrationPlugin } from "./integration-plugin";
2+export type { McpIntegrationPluginOptions } from "./integration-plugin";
33 export { McpClient } from "./client";
44 export {
55 probeMcpEndpoint,
packages/plugins/mcp/src/react/integration-plugin.tsxrenamed from packages/plugins/mcp/src/react/source-plugin.tsx+5−5
@@ -2,11 +2,11 @@ import { lazy, type ComponentProps, type ComponentType } from "react"; · 1 unmodified line
22 import type { IntegrationPlugin } from "@executor-js/sdk/client";
33 import { mcpPresets } from "../sdk/presets";
44
5−const importAdd = () => import("./AddMcpSource");
6−const importEditSheet = () => import("./EditMcpSource");
5+const importAdd = () => import("./AddMcpIntegration");
6+const importEditSheet = () => import("./EditMcpIntegration");
77 const importAccounts = () => import("./McpAccountsPanel");
88
9−const LazyAddMcpSource = lazy(importAdd);
9+const LazyAddMcpIntegration = lazy(importAdd);
1010 const LazyEditMcpSheet = lazy(importEditSheet);
1111 const LazyMcpAccountsPanel = lazy(importAccounts);
1212
@@ -14,7 +14,7 @@ type AddProps = ComponentProps<IntegrationPlugin["add"]>; · 1 unmodified line
1414
1515 export interface McpIntegrationPluginOptions {
1616 /**
17− * Enable the stdio transport in the add-source UI (tab + presets).
17+ * Enable the stdio transport in the add-integration UI (tab + presets).
1818 *
1919 * Off by default — stdio is a high-risk transport on any server deployment
2020 * (see `dangerouslyAllowStdioMCP` on the server-side plugin). Only enable in
@@ -29,7 +29,7 @@ export const createMcpIntegrationPlugin = ( · 8 unmodified lines
2929 const allowStdio = options?.allowStdio ?? false;
3030
3131 const AddWithFlag: ComponentType<AddProps> = (props) => (
32− <LazyAddMcpSource {...props} allowStdio={allowStdio} />
32+ <LazyAddMcpIntegration {...props} allowStdio={allowStdio} />
3333 );
3434
3535 const presets = allowStdio
packages/plugins/mcp/src/react/plugin-client.tsx+2−2
@@ -12,12 +12,12 @@ · 11 unmodified lines
1212
1313 import { defineClientPlugin } from "@executor-js/sdk/client";
1414
15−import { createMcpIntegrationPlugin } from "./source-plugin";
15+import { createMcpIntegrationPlugin } from "./integration-plugin";
1616
1717 export interface McpClientConfig {
1818 /**
1919 * Mirrors `dangerouslyAllowStdioMCP` on the server-side plugin. When
20− * false, the AddMcpSource UI hides the stdio tab and stdio presets.
20+ * false, the AddMcpIntegration UI hides the stdio tab and stdio presets.
2121 * Defaults to false — same default as the server flag.
2222 */
2323 readonly allowStdio?: boolean;
packages/plugins/mcp/src/sdk/plugin.test.ts+1−1
@@ -624,7 +624,7 @@ describe("mcpPlugin", () => { · 623 unmodified lines
624624 retryable: false,
625625 details: {
626626 category: "authentication",
627− source: { id: slug },
627+ integration: { id: slug },
628628 credential: { kind: "upstream", label: "main" },
629629 upstream: { status },
630630 },
packages/plugins/mcp/src/sdk/plugin.ts+5−5
@@ -299,7 +299,7 @@ const mcpInvocationAuthFailure = (input: { · 298 unmodified lines
299299 input.status === 403
300300 ? `MCP server rejected connection "${input.connection}" with HTTP 403. The credential may lack access or required scope; re-authenticate or update the connection before retrying this tool.`
301301 : `MCP server rejected connection "${input.connection}" with HTTP 401. Re-authenticate or update the connection before retrying this tool.`,
302− source: { id: input.integration },
302+ integration: { id: input.integration },
303303 credential: { kind: "upstream", label: input.connection },
304304 status: input.status,
305305 upstream: { status: input.status },
@@ -312,7 +312,7 @@ const mcpInvocationOAuthReauthFailure = (input: { · 6 unmodified lines
312312 authToolFailure({
313313 code: "oauth_reauth_required",
314314 message: `OAuth connection "${input.connection}" requires reauthorization before retrying this MCP tool.`,
315− source: { id: input.integration },
315+ integration: { id: input.integration },
316316 credential: { kind: "oauth", label: input.connection },
317317 });
318318
@@ -1244,7 +1244,7 @@ export const mcpPlugin = definePlugin((options?: McpPluginOptions) => { · 925 unmodified lines
12441244 return authToolFailure({
12451245 code: "connection_value_missing",
12461246 message: `Connection has no resolvable credential value for input(s): ${missing.join(", ")}. Re-create the connection with the required value(s).`,
1247− source: { id: String(credential.integration) },
1247+ integration: { id: String(credential.integration) },
12481248 credential: { kind: "upstream", label: String(credential.connection) },
12491249 });
12501250 }
@@ -1335,7 +1335,7 @@ export const mcpPlugin = definePlugin((options?: McpPluginOptions) => { · 84 unmodified lines
13351335 authToolFailure({
13361336 code: "connection_rejected",
13371337 message: error.message,
1338− source: { id: String(credential.integration) },
1338+ integration: { id: String(credential.integration) },
13391339 credential: { kind: "upstream", label: String(credential.connection) },
13401340 }),
13411341 );
@@ -1518,7 +1518,7 @@ export const mcpPlugin = definePlugin((options?: McpPluginOptions) => { · 176 unmodified lines
15181518 }),
15191519 },
15201520
1521− staticSources: (self) => [
1521+ staticIntegrations: (self) => [
15221522 {
15231523 id: "mcp",
15241524 kind: "executor",
packages/plugins/mcp/src/sdk/probe-shape-real-servers.live.test.ts+1−1
@@ -151,7 +151,7 @@ const live = process.env.MCP_PROBE_LIVE === "1"; · 150 unmodified lines
151151 // Run the full probe path (the function the React UI calls). The result
152152 // surfaces `requiresOAuth` / `supportsDynamicRegistration`, which is what
153153 // drives the OAuth popup vs. credentials-editor branches in
154−// AddMcpSource. For OAuth-protected servers we want this to be `true`;
154+// AddMcpIntegration. For OAuth-protected servers we want this to be `true`;
155155 // for API-key MCPs (Cubic) it should fail with the auth-required
156156 // message; for unauth public servers (Hugging Face, etc.) it should
157157 // succeed with `requiresOAuth: false` and a tool count.
packages/plugins/onepassword/src/sdk/plugin.ts+1−1
@@ -336,7 +336,7 @@ export const onepasswordPlugin = definePlugin((options?: OnePasswordPluginOption · 335 unmodified lines
336336
337337 extension: (ctx) => makeOnePasswordExtension(ctx, timeoutMs, preferSdk),
338338
339− staticSources: (self) => [
339+ staticIntegrations: (self) => [
340340 {
341341 id: "onepassword",
342342 kind: "executor",
packages/plugins/openapi/README.md+1−1
@@ -35,7 +35,7 @@ const result = await executor.tools.invoke("petstore.listPets", {}); · 34 unmodified lines
3535
3636 ## Secret-backed auth headers
3737
38−Wire API keys or bearer tokens through the executor's secret store — never hard-code them in source configs:
38+Wire API keys or bearer tokens through the executor's secret store — never hard-code them in integration configs:
3939
4040 ```ts
4141 import { createExecutor } from "@executor-js/sdk";
packages/plugins/openapi/src/api/group.ts+1−1
@@ -134,7 +134,7 @@ const IntegrationView = Schema.Struct({ · 133 unmodified lines
134134 // deliberately NOT served: it's a multi-MB build artifact in the plugin blob
135135 // store, and no client reads it (the configure UI only touches the template).
136136 const OpenApiConfigView = Schema.Struct({
137− sourceUrl: Schema.optional(Schema.String),
137+ specUrl: Schema.optional(Schema.String),
138138 baseUrl: Schema.optional(Schema.String),
139139 headers: Schema.optional(Schema.Record(Schema.String, Schema.String)),
140140 queryParams: Schema.optional(Schema.Record(Schema.String, Schema.String)),
packages/plugins/openapi/src/api/handlers.ts+1−1
@@ -96,7 +96,7 @@ export const OpenApiHandlers = HttpApiBuilder.group(ExecutorApiWithOpenApi, "ope · 95 unmodified lines
9696 const config = yield* ext.getConfig(params.slug);
9797 return config
9898 ? {
99− sourceUrl: config.sourceUrl,
99+ specUrl: config.specUrl,
100100 baseUrl: config.baseUrl,
101101 headers: config.headers ? { ...config.headers } : undefined,
102102 queryParams: config.queryParams ? { ...config.queryParams } : undefined,
packages/plugins/openapi/src/providers/google/config.ts+1−1
@@ -5,7 +5,7 @@ import type { Authentication } from "../../sdk/types"; · 4 unmodified lines
55
66 export const GoogleIntegrationConfigSchema = Schema.Struct({
77 specHash: Schema.optional(Schema.String),
8− sourceUrl: Schema.optional(Schema.String),
8+ specUrl: Schema.optional(Schema.String),
99 googleDiscoveryUrls: Schema.optional(Schema.Array(Schema.String)),
1010 baseUrl: Schema.optional(Schema.String),
1111 headers: Schema.optional(Schema.Record(Schema.String, Schema.String)),
packages/plugins/openapi/src/providers/google/discovery.test.ts+1−1
@@ -723,7 +723,7 @@ it.effect( · 722 unmodified lines
723723 }),
724724 );
725725
726−it.effect("bundles Google Discovery documents into one Google OpenAPI source", () =>
726+it.effect("bundles Google Discovery documents into one Google OpenAPI integration", () =>
727727 Effect.gen(function* () {
728728 const result = yield* convertGoogleDiscoveryBundleToOpenApi({
729729 documents: [
packages/plugins/openapi/src/providers/google/openapi-ownership-migration.test.ts+1−1
@@ -180,7 +180,7 @@ describe("runSqliteGoogleOpenApiOwnershipMigration", () => { · 179 unmodified lines
180180 pluginId: "openapi",
181181 config: {
182182 specHash: "stripehash",
183− sourceUrl: "https://stripe.example/openapi.json",
183+ specUrl: "https://stripe.example/openapi.json",
184184 },
185185 }),
186186 );
packages/plugins/openapi/src/providers/google/presets.ts+1−1
@@ -33,7 +33,7 @@ export const GOOGLE_PHOTOS_ICON = · 32 unmodified lines
3333 export const googleOpenApiBundlePreset: GooglePreset = {
3434 id: GOOGLE_BUNDLE_PRESET_ID,
3535 name: "Google",
36− summary: "Bundle Gmail, Calendar, Drive, Docs, and other Google APIs into one source.",
36+ summary: "Bundle Gmail, Calendar, Drive, Docs, and other Google APIs into one integration.",
3737 icon: GOOGLE_G,
3838 featured: true,
3939 };
packages/plugins/openapi/src/providers/google/spec-format-adapter.ts+1−1
@@ -75,7 +75,7 @@ export const googleDiscoveryAdapter: SpecFormatAdapter = { · 74 unmodified lines
7575 );
7676 return {
7777 specText: conversion.specText,
78− sourceUrl: documents[0]?.discoveryUrl,
78+ specUrl: documents[0]?.discoveryUrl,
7979 baseUrl: conversion.baseUrl,
8080 authenticationTemplate: conversion.authenticationTemplate,
8181 document,
packages/plugins/openapi/src/providers/microsoft/graph.ts+2−2
@@ -86,7 +86,7 @@ export type MicrosoftGraphIntegrationConfig = OpenApiIntegrationConfig & { · 85 unmodified lines
8686
8787 const MicrosoftGraphIntegrationConfigSchema = Schema.Struct({
8888 specHash: Schema.optional(Schema.String),
89− sourceUrl: Schema.optional(Schema.String),
89+ specUrl: Schema.optional(Schema.String),
9090 baseUrl: Schema.optional(Schema.String),
9191 headers: Schema.optional(Schema.Record(Schema.String, Schema.String)),
9292 queryParams: Schema.optional(Schema.Record(Schema.String, Schema.String)),
@@ -298,7 +298,7 @@ const validateSelectionUrls = ( · 205 unmodified lines
298298 const specUrl = normalizeMicrosoftGraphSpecUrl(selection.specUrl, policy);
299299 if (!specUrl) {
300300 return yield* new OpenApiParseError({
301− message: "Microsoft Graph specUrl must point to the trusted Microsoft Graph OpenAPI source",
301+ message: "Microsoft Graph specUrl must point to the trusted Microsoft Graph OpenAPI spec",
302302 });
303303 }
304304 const baseUrl = normalizeMicrosoftGraphBaseUrl(selection.baseUrl, policy);
packages/plugins/openapi/src/providers/microsoft/presets.ts+1−1
@@ -59,7 +59,7 @@ export const MICROSOFT_GRAPH_PRESET_ID = "microsoft"; · 58 unmodified lines
5959 export const microsoftGraphPreset: MicrosoftGraphPreset = {
6060 id: MICROSOFT_GRAPH_PRESET_ID,
6161 name: "Microsoft Graph",
62− summary: "Bundle Microsoft 365 workloads into one Graph source and one OAuth consent.",
62+ summary: "Bundle Microsoft 365 workloads into one Graph integration and one OAuth consent.",
6363 icon: MICROSOFT_ICON,
6464 featured: true,
6565 };
packages/plugins/openapi/src/providers/microsoft/spec-format-adapter.ts+1−1
@@ -28,7 +28,7 @@ export const microsoftGraphAdapter: SpecFormatAdapter = { · 27 unmodified lines
2828 ).pipe(
2929 Effect.map((graphSpec) => ({
3030 specText: graphSpec.specText,
31− sourceUrl: graphSpec.specUrl,
31+ specUrl: graphSpec.specUrl,
3232 baseUrl: graphSpec.baseUrl,
3333 authenticationTemplate: graphSpec.authenticationTemplate,
3434 // Stream the full Graph source straight to persisted bindings. This is
packages/plugins/openapi/src/react/AddOpenApiIntegration.test.tsrenamed from packages/plugins/openapi/src/react/AddOpenApiSource.test.ts+1−1
@@ -5,7 +5,7 @@ import { · 4 unmodified lines
55 AddOpenApiHealthCheckSection,
66 baseUrlFromSpecInput,
77 openApiPreviewFailureMessage,
8−} from "./AddOpenApiSource";
8+} from "./AddOpenApiIntegration";
99
1010 const visibleText = (node: React.ReactNode): string => {
1111 if (node === null || node === undefined || typeof node === "boolean") return "";
packages/plugins/openapi/src/react/AddOpenApiIntegration.tsxrenamed from packages/plugins/openapi/src/react/AddOpenApiSource.tsx+16−10
@@ -44,7 +44,7 @@ import { · 43 unmodified lines
4444 openApiWireAuthInput,
4545 } from "./auth-method-config";
4646 import { addOpenApiSpec, previewOpenApiSpec } from "./atoms";
47−import { OpenApiSourceDetailsFields } from "./OpenApiSourceDetailsFields";
47+import { OpenApiIntegrationDetailsFields } from "./OpenApiIntegrationDetailsFields";
4848 import type { SpecPreviewSummary } from "../sdk/preview";
4949 import { type Authentication } from "../sdk/types";
5050 import { resolveServerUrl } from "../sdk/openapi-utils";
@@ -142,7 +142,7 @@ export function AddOpenApiHealthCheckSection(props: { · 91 unmodified lines
142142 // flow no longer creates a connection.
143143 // ---------------------------------------------------------------------------
144144
145−export default function AddOpenApiSource(props: {
145+export default function AddOpenApiIntegration(props: {
146146 onComplete: (slug?: string) => void;
147147 onCancel: () => void;
148148 initialUrl?: string;
@@ -222,13 +222,15 @@ export default function AddOpenApiSource(props: { · 73 unmodified lines
222222 )?.icon ?? null;
223223
224224 const resolvedBaseUrl = baseUrl.trim();
225− const resolvedSourceId =
225+ const resolvedIntegrationId =
226226 initialPreset?.defaultSlug ||
227227 slugifyNamespace(identity.namespace) ||
228228 (preview ? Option.getOrElse(preview.title, () => "openapi") : "openapi");
229229 const resolvedDisplayName =
230230 identity.name.trim() ||
231− (preview ? Option.getOrElse(preview.title, () => resolvedSourceId) : resolvedSourceId);
231+ (preview
232+ ? Option.getOrElse(preview.title, () => resolvedIntegrationId)
233+ : resolvedIntegrationId);
232234 const resolvedDescription =
233235 descriptionDraft ?? (preview ? Option.getOrElse(preview.description, () => "") : "");
234236
@@ -324,7 +326,7 @@ export default function AddOpenApiSource(props: { · 89 unmodified lines
324326 // Pre-empt the API's `IntegrationAlreadyExistsError`: adding an integration
325327 // whose slug already exists clobbers the existing one's connections/policies,
326328 // so the API blocks it. Surface that here from the tenant-scoped catalog list.
327− const slugAlreadyExists = useSlugAlreadyExists(resolvedSourceId);
329+ const slugAlreadyExists = useSlugAlreadyExists(resolvedIntegrationId);
328330
329331 // The base URL is optional when the spec declares servers (resolved per call);
330332 // required only when it doesn't. A drafted health check must have its required
@@ -364,7 +366,7 @@ export default function AddOpenApiSource(props: { · 33 unmodified lines
364366 const exit = await doAdd({
365367 payload: {
366368 spec: specInputForAdd(specUrl),
367− slug: resolvedSourceId,
369+ slug: resolvedIntegrationId,
368370 name: resolvedDisplayName,
369371 ...(resolvedDescription.trim().length > 0
370372 ? { description: resolvedDescription.trim() }
@@ -383,14 +385,16 @@ export default function AddOpenApiSource(props: { · 12 unmodified lines
383385 reactivityKeys: integrationWriteKeys,
384386 });
385387 if (Exit.isFailure(exit)) {
386− setAddError(addIntegrationErrorMessage(exit, resolvedSourceId, "Failed to add integration"));
388+ setAddError(
389+ addIntegrationErrorMessage(exit, resolvedIntegrationId, "Failed to add integration"),
390+ );
387391 return null;
388392 }
389393 return exit.value.slug;
390394 }, [
391395 specUrl,
392396 doAdd,
393− resolvedSourceId,
397+ resolvedIntegrationId,
394398 resolvedDisplayName,
395399 resolvedDescription,
396400 resolvedBaseUrl,
@@ -499,7 +503,7 @@ export default function AddOpenApiSource(props: { · 102 unmodified lines
499503 ) : null}
500504
501505 {preview ? (
502− <OpenApiSourceDetailsFields
506+ <OpenApiIntegrationDetailsFields
503507 title={Option.getOrElse(preview.title, () => "API")}
504508 subtitle={`${Option.getOrElse(preview.version, () => "")}${
505509 Option.isSome(preview.version) ? " · " : ""
@@ -558,7 +562,9 @@ export default function AddOpenApiSource(props: { · 52 unmodified lines
558562 />
559563 ) : null}
560564
561− {preview && slugAlreadyExists && !adding && <SlugCollisionAlert slug={resolvedSourceId} />}
565+ {preview && slugAlreadyExists && !adding && (
566+ <SlugCollisionAlert slug={resolvedIntegrationId} />
567+ )}
562568
563569 {addError && <FormErrorAlert message={addError} />}
564570
packages/plugins/openapi/src/react/OpenApiAccountsPanel.tsx+3−3
@@ -51,12 +51,12 @@ const NO_AUTH_METHOD: AuthMethod = { · 50 unmodified lines
5151 // ---------------------------------------------------------------------------
5252
5353 export default function OpenApiAccountsPanel(props: {
54− readonly sourceId: string;
54+ readonly integrationId: string;
5555 readonly integrationName: string;
5656 readonly accountHandoff?: IntegrationAccountHandoff | null;
5757 }) {
58− const { sourceId, integrationName, accountHandoff } = props;
59− const slug = IntegrationSlug.make(sourceId);
58+ const { integrationId, integrationName, accountHandoff } = props;
59+ const slug = IntegrationSlug.make(integrationId);
6060 const configResult = useAtomValue(openApiConfigAtom(slug));
6161 const doConfigure = useAtomSet(openapiConfigure, { mode: "promiseExit" });
6262
packages/plugins/openapi/src/react/OpenApiIntegrationDetailsFields.tsxrenamed from packages/plugins/openapi/src/react/OpenApiSourceDetailsFields.tsx+1−1
@@ -23,7 +23,7 @@ import { · 22 unmodified lines
2323 * URL; pasted document content gets a multi-line editor instead. */
2424 const isUrlInput = (value: string): boolean => URL.canParse(value.trim());
2525
26−export function OpenApiSourceDetailsFields(props: {
26+export function OpenApiIntegrationDetailsFields(props: {
2727 readonly title: string;
2828 readonly subtitle?: string;
2929 readonly identity: IntegrationIdentity;
packages/plugins/openapi/src/react/UpdateSpecSection.tsx+4−4
@@ -42,7 +42,7 @@ const outcomeSummary = (outcome: UpdateOutcome): string => { · 41 unmodified lines
4242 };
4343
4444 export default function UpdateSpecSection(props: EditSheetSectionProps) {
45− const slug = IntegrationSlug.make(props.sourceId);
45+ const slug = IntegrationSlug.make(props.integrationId);
4646 const configResult = useAtomValue(openApiConfigAtom(slug));
4747 const doUpdate = useAtomSet(updateOpenApiSpec, { mode: "promiseExit" });
4848 const [refetchStaged, setRefetchStaged] = useState(false);
@@ -52,7 +52,7 @@ export default function UpdateSpecSection(props: EditSheetSectionProps) { · 3 unmodified lines
5252 const config =
5353 AsyncResult.isSuccess(configResult) && configResult.value ? configResult.value : null;
5454
55− const sourceUrl = config?.sourceUrl;
55+ const specUrl = config?.specUrl;
5656
5757 // The staged apply, rebuilt whenever the staged inputs change. Reported to
5858 // the sheet through a ref-stable callback so Save can run it.
@@ -95,7 +95,7 @@ export default function UpdateSpecSection(props: EditSheetSectionProps) { · 36 unmodified lines
9595 </p>
9696 </div>
9797
98− {sourceUrl ? (
98+ {specUrl ? (
9999 <div className="space-y-2">
100100 <Label className="flex items-start gap-2 text-sm font-normal">
101101 <Checkbox
@@ -107,7 +107,7 @@ export default function UpdateSpecSection(props: EditSheetSectionProps) { · 5 unmodified lines
107107 <span className="space-y-0.5">
108108 <span className="block">Re-fetch the spec on save</span>
109109 <span className="block break-all font-mono text-xs text-muted-foreground">
110− {sourceUrl}
110+ {specUrl}
111111 </span>
112112 </span>
113113 </Label>
packages/plugins/openapi/src/react/index.ts+2−2
@@ -1,6 +1,6 @@
1−export { openApiIntegrationPlugin } from "./source-plugin";
1+export { openApiIntegrationPlugin } from "./integration-plugin";
22 export { OpenApiClient } from "./client";
3−export { OpenApiSourceDetailsFields } from "./OpenApiSourceDetailsFields";
3+export { OpenApiIntegrationDetailsFields } from "./OpenApiIntegrationDetailsFields";
44 export {
55 authenticationFromEditorValue,
66 authMethodsFromConfig,
packages/plugins/openapi/src/react/integration-plugin.tsrenamed from packages/plugins/openapi/src/react/source-plugin.ts+1−1
@@ -2,7 +2,7 @@ import { lazy } from "react"; · 1 unmodified line
22 import type { IntegrationPlugin, IntegrationPreset } from "@executor-js/sdk/client";
33 import { openApiPresets } from "../sdk/presets";
44
5−const importAdd = () => import("./AddOpenApiSource");
5+const importAdd = () => import("./AddOpenApiIntegration");
66 const importEditSheet = () => import("./UpdateSpecSection");
77 const importAccounts = () => import("./OpenApiAccountsPanel");
88
packages/plugins/openapi/src/react/plugin-client.tsx+2−2
@@ -3,7 +3,7 @@ · 2 unmodified lines
33 //
44 // Aggregates the openapi plugin's frontend contributions into a single
55 // declarative spec. The host's Vite plugin reads this via
6−// `virtual:executor/plugins-client`, so the host's sources page derives
6+// `virtual:executor/plugins-client`, so the host's integrations page derives
77 // the openapi entry from here without a direct `*/react` import.
88 //
99 // The richer add/edit/summary components still live in `./react`; this
@@ -12,7 +12,7 @@ · 2 unmodified lines
1212
1313 import { defineClientPlugin } from "@executor-js/sdk/client";
1414
15−import { createOpenApiIntegrationPlugin, type OpenApiClientConfig } from "./source-plugin";
15+import { createOpenApiIntegrationPlugin, type OpenApiClientConfig } from "./integration-plugin";
1616
1717 export default function createOpenApiClientPlugin(config?: OpenApiClientConfig) {
1818 return defineClientPlugin({
packages/plugins/openapi/src/sdk/backing.ts+1−1
@@ -141,7 +141,7 @@ const openApiAuthToolFailure = (failure: { · 140 unmodified lines
141141 authToolFailure({
142142 code: failure.code as Parameters<typeof authToolFailure>[0]["code"],
143143 message: failure.message,
144− source: { id: failure.integration, scope: failure.owner },
144+ integration: { id: failure.integration, scope: failure.owner },
145145 credential: {
146146 kind: failure.credentialKind,
147147 ...(failure.credentialLabel ? { label: failure.credentialLabel } : {}),
packages/plugins/openapi/src/sdk/config.ts+1−1
@@ -44,7 +44,7 @@ export const OpenApiIntegrationConfigSchema = Schema.Struct({ · 43 unmodified lines
4444 * blob (`spec/<hash>` in the plugin blob store). */
4545 specHash: Schema.optional(Schema.String),
4646 /** Origin URL the spec was fetched from, when known. Enables refresh. */
47− sourceUrl: Schema.optional(Schema.String),
47+ specUrl: Schema.optional(Schema.String),
4848 /** Optional base URL override. */
4949 baseUrl: Schema.optional(Schema.String),
5050 /** Static headers applied to every request (no secret material). */
packages/plugins/openapi/src/sdk/configure.test.ts+2−2
@@ -31,7 +31,7 @@ import { openApiPlugin } from "./plugin"; · 30 unmodified lines
3131 import type { APIKeyAuthentication, Authentication, AuthenticationInput } from "./types";
3232 import { variable } from "@executor-js/sdk/http-auth";
3333 import {
34− makeOpenApiHttpApiTestSourceConfig,
34+ makeOpenApiHttpApiTestIntegrationConfig,
3535 serveOpenApiHttpApiTestServer,
3636 unwrapInvocation,
3737 } from "../testing";
@@ -66,7 +66,7 @@ const servePluginTestApi = () => · 28 unmodified lines
6666 serveOpenApiHttpApiTestServer({ api: TestApi, handlersLayer: EchoGroupLive });
6767
6868 const specText = () => {
69− const spec = makeOpenApiHttpApiTestSourceConfig(TestApi, {}).spec;
69+ const spec = makeOpenApiHttpApiTestIntegrationConfig(TestApi, {}).spec;
7070 if (spec.kind === "blob") return spec.value;
7171 return spec.url;
7272 };
packages/plugins/openapi/src/sdk/describe-auth-methods.test.ts+3−3
@@ -106,20 +106,20 @@ describe("describeOpenApiAuthMethods", () => { · 105 unmodified lines
106106 ...recordWith([]),
107107 config: {
108108 spec: "{}",
109− sourceUrl: "https://api.example.com/openapi.json",
109+ specUrl: "https://api.example.com/openapi.json",
110110 baseUrl: "https://api.example.com",
111111 } as IntegrationConfig,
112112 }),
113113 ).toEqual({ url: "https://api.example.com" });
114114 });
115115
116− it("falls back to sourceUrl for display metadata", () => {
116+ it("falls back to specUrl for display metadata", () => {
117117 expect(
118118 describeOpenApiIntegrationDisplay({
119119 ...recordWith([]),
120120 config: {
121121 spec: "{}",
122− sourceUrl: "https://api.example.com/openapi.json",
122+ specUrl: "https://api.example.com/openapi.json",
123123 } as IntegrationConfig,
124124 }),
125125 ).toEqual({ url: "https://api.example.com/openapi.json" });
packages/plugins/openapi/src/sdk/invoke.ts+6−6
@@ -839,7 +839,7 @@ export const buildRequest = Effect.fn("OpenApi.buildRequest")(function* ( · 838 unmodified lines
839839 operation: OperationBinding,
840840 args: Record<string, unknown>,
841841 resolvedHeaders: Record<string, string>,
842− sourceQueryParams: Record<string, string> = {},
842+ integrationQueryParams: Record<string, string> = {},
843843 ) {
844844 const resolvedPath = yield* resolvePath(operation.pathTemplate, args, operation.parameters);
845845
@@ -851,7 +851,7 @@ export const buildRequest = Effect.fn("OpenApi.buildRequest")(function* ( · 5 unmodified lines
851851 // can override it; the upstream still gets a User-Agent if nothing else sets one.
852852 request = HttpClientRequest.setHeader(request, "User-Agent", DEFAULT_USER_AGENT);
853853
854− for (const [name, value] of Object.entries(sourceQueryParams)) {
854+ for (const [name, value] of Object.entries(integrationQueryParams)) {
855855 request = HttpClientRequest.setUrlParam(request, name, value);
856856 }
857857
@@ -998,7 +998,7 @@ export const invoke = Effect.fn("OpenApi.invoke")(function* ( · 140 unmodified lines
998998 operation: OperationBinding,
999999 args: Record<string, unknown>,
10001000 resolvedHeaders: Record<string, string>,
1001− sourceQueryParams: Record<string, string> = {},
1001+ integrationQueryParams: Record<string, string> = {},
10021002 options: InvokeOptions = {},
10031003 ) {
10041004 const client = yield* HttpClient.HttpClient;
@@ -1011,7 +1011,7 @@ export const invoke = Effect.fn("OpenApi.invoke")(function* ( · 6 unmodified lines
10111011 "plugin.openapi.headers.resolved_count": Object.keys(resolvedHeaders).length,
10121012 });
10131013
1014− const request = yield* buildRequest(operation, args, resolvedHeaders, sourceQueryParams);
1014+ const request = yield* buildRequest(operation, args, resolvedHeaders, integrationQueryParams);
10151015
10161016 const responseHeadersTimeoutMs = options.responseHeadersTimeoutMs ?? RESPONSE_HEADERS_TIMEOUT_MS;
10171017 const runFork = Effect.runForkWith(yield* Effect.context<never>());
@@ -1161,7 +1161,7 @@ export const invokeWithLayer = ( · 143 unmodified lines
11611161 args: Record<string, unknown>,
11621162 baseUrl: string,
11631163 resolvedHeaders: Record<string, string>,
1164− sourceQueryParams: Record<string, string>,
1164+ integrationQueryParams: Record<string, string>,
11651165 httpClientLayer: Layer.Layer<HttpClient.HttpClient, never, never>,
11661166 options: InvokeOptions = {},
11671167 ) => {
@@ -1176,7 +1176,7 @@ export const invokeWithLayer = ( · 8 unmodified lines
11761176 ).pipe(Layer.provide(httpClientLayer))
11771177 : httpClientLayer;
11781178
1179− return invoke(operation, args, resolvedHeaders, sourceQueryParams, options).pipe(
1179+ return invoke(operation, args, resolvedHeaders, integrationQueryParams, options).pipe(
11801180 Effect.provide(clientWithBaseUrl),
11811181 // `invoke` annotates http.status_code on ITS span (`OpenApi.invoke`,
11821182 // via Effect.fn) — annotateCurrentSpan inside it never reaches this
packages/plugins/openapi/src/sdk/plugin.test.ts+2−2
@@ -41,7 +41,7 @@ import { openApiPlugin } from "./plugin"; · 40 unmodified lines
4141 import { type AuthenticationInput } from "./types";
4242 import {
4343 addOpenApiTestConnection,
44− makeOpenApiHttpApiTestSourceConfig,
44+ makeOpenApiHttpApiTestIntegrationConfig,
4545 serveMutableOpenApiSpecTestServer,
4646 serveOpenApiHttpApiTestServer,
4747 unwrapInvocation,
@@ -104,7 +104,7 @@ const ItemsGroup = HttpApiGroup.make("items") · 56 unmodified lines
104104 const TestApi = HttpApi.make("testApi").add(ItemsGroup);
105105
106106 const testApiSpecText = () => {
107− const spec = makeOpenApiHttpApiTestSourceConfig(TestApi, {}).spec;
107+ const spec = makeOpenApiHttpApiTestIntegrationConfig(TestApi, {}).spec;
108108 if (spec.kind === "blob") return spec.value;
109109 return spec.url;
110110 };
packages/plugins/openapi/src/sdk/plugin.ts+26−26
@@ -122,7 +122,7 @@ export interface UpdateSpecResult { · 121 unmodified lines
122122
123123 export interface OpenApiUpdateSpecInput {
124124 /** New spec source. Omit to re-fetch from the integration's stored
125− * `sourceUrl`. */
125+ * `specUrl`. */
126126 readonly spec?: OpenApiSpecInput;
127127 }
128128
@@ -273,7 +273,7 @@ const AuthenticationSchema = Schema.Union([ · 144 unmodified lines
273273 ApiKeyAuthTemplate,
274274 ]);
275275
276−const AddSourceInputSchema = Schema.Struct({
276+const AddIntegrationInputSchema = Schema.Struct({
277277 spec: OpenApiSpecInputSchema,
278278 slug: Schema.optional(Schema.String),
279279 name: Schema.optional(Schema.String),
@@ -287,7 +287,7 @@ const AddSourceInputSchema = Schema.Struct({ · 7 unmodified lines
287287 authenticationTemplate: Schema.optional(Schema.Array(AuthenticationSchema)),
288288 });
289289
290−const AddSourceOutputSchema = Schema.Struct({
290+const AddIntegrationOutputSchema = Schema.Struct({
291291 slug: Schema.String,
292292 toolCount: Schema.Number,
293293 });
@@ -298,11 +298,11 @@ const PreviewSpecInputStandardSchema = Schema.toStandardSchemaV1( · 4 unmodified lines
298298 const PreviewSpecOutputStandardSchema = Schema.toStandardSchemaV1(
299299 Schema.toStandardJSONSchemaV1(StaticPreviewSpecOutputSchema),
300300 );
301−const AddSourceInputStandardSchema = Schema.toStandardSchemaV1(
302− Schema.toStandardJSONSchemaV1(AddSourceInputSchema),
301+const AddIntegrationInputStandardSchema = Schema.toStandardSchemaV1(
302+ Schema.toStandardJSONSchemaV1(AddIntegrationInputSchema),
303303 );
304−const AddSourceOutputStandardSchema = Schema.toStandardSchemaV1(
305− Schema.toStandardJSONSchemaV1(AddSourceOutputSchema),
304+const AddIntegrationOutputStandardSchema = Schema.toStandardSchemaV1(
305+ Schema.toStandardJSONSchemaV1(AddIntegrationOutputSchema),
306306 );
307307
308308 const openApiToolFailure = (code: string, message: string, details?: unknown) =>
@@ -378,7 +378,7 @@ const staticPreviewOutput = (preview: SpecPreview): StaticPreviewSpecOutput => ( · 69 unmodified lines
378378 })),
379379 });
380380
381−const specInputToSourceUrl = (spec: OpenApiSpecInput): string | undefined =>
381+const specInputToSpecUrl = (spec: OpenApiSpecInput): string | undefined =>
382382 spec.kind === "url" ? spec.url : undefined;
383383
384384 const OAUTH_DISCOVERED_SCHEME_NAME = "DiscoveredOAuth2";
@@ -418,7 +418,7 @@ const addProbeCandidate = (candidates: string[], value: string | undefined): voi · 33 unmodified lines
418418
419419 const oauthProbeCandidates = (
420420 preview: SpecPreview,
421− sourceUrl: string | undefined,
421+ specUrl: string | undefined,
422422 baseUrl: string | undefined,
423423 ): readonly string[] => {
424424 const candidates: string[] = [];
@@ -429,7 +429,7 @@ const oauthProbeCandidates = ( · 4 unmodified lines
429429 resolveServerUrl(server.url, Option.getOrUndefined(server.variables), {}),
430430 );
431431 }
432− addProbeCandidate(candidates, sourceUrl);
432+ addProbeCandidate(candidates, specUrl);
433433 return candidates;
434434 };
435435
@@ -574,7 +574,7 @@ export const describeOpenApiIntegrationDisplay = ( · 138 unmodified lines
574574 ): { readonly url?: string; readonly family?: string } => {
575575 const config = decodeOpenApiIntegrationConfig(record.config);
576576 return {
577− url: config?.baseUrl ?? config?.sourceUrl,
577+ url: config?.baseUrl ?? config?.specUrl,
578578 ...(config?.family ? { family: config.family } : {}),
579579 };
580580 };
@@ -624,7 +624,7 @@ export const openApiPlugin = definePlugin< · 43 unmodified lines
624624 const specText = yield* resolveSpecText(config.spec.url).pipe(
625625 Effect.provide(httpClientLayer),
626626 );
627− return { specText, sourceUrl: config.spec.url };
627+ return { specText, specUrl: config.spec.url };
628628 }
629629 return { specText: config.spec.value };
630630 });
@@ -654,13 +654,13 @@ export const openApiPlugin = definePlugin< · 23 unmodified lines
654654 const enrichPreviewWithDiscoveredOAuth = (input: {
655655 readonly specText: string;
656656 readonly preview: SpecPreview;
657− readonly sourceUrl?: string;
657+ readonly specUrl?: string;
658658 readonly baseUrl?: string;
659659 }): Effect.Effect<SpecPreview, OpenApiParseError | OpenApiExtractionError> =>
660660 Effect.gen(function* () {
661661 if (input.preview.oauth2Presets.length > 0) return input.preview;
662662
663− const candidates = oauthProbeCandidates(input.preview, input.sourceUrl, input.baseUrl);
663+ const candidates = oauthProbeCandidates(input.preview, input.specUrl, input.baseUrl);
664664 if (candidates.length === 0) return input.preview;
665665
666666 for (const candidate of candidates) {
@@ -741,7 +741,7 @@ export const openApiPlugin = definePlugin< · 74 unmodified lines
741741 enrichPreviewWithDiscoveredOAuth({
742742 specText: resolved.specText,
743743 preview: rawPreview,
744− sourceUrl: resolved.sourceUrl ?? specInputToSourceUrl(config.spec),
744+ specUrl: resolved.specUrl ?? specInputToSpecUrl(config.spec),
745745 baseUrl: explicitBaseUrl,
746746 }),
747747 ),
@@ -772,8 +772,8 @@ export const openApiPlugin = definePlugin< · 24 unmodified lines
772772 const integrationConfig: OpenApiIntegrationConfig = {
773773 ...(resolved.config ?? {}),
774774 specHash,
775− ...((resolved.sourceUrl ?? specInputToSourceUrl(config.spec)) !== undefined
776− ? { sourceUrl: resolved.sourceUrl ?? specInputToSourceUrl(config.spec) }
775+ ...((resolved.specUrl ?? specInputToSpecUrl(config.spec)) !== undefined
776+ ? { specUrl: resolved.specUrl ?? specInputToSpecUrl(config.spec) }
777777 : {}),
778778 // baseUrl is an optional override only. The host is otherwise
779779 // resolved per call from the operation's `servers` (extracted from
@@ -822,7 +822,7 @@ export const openApiPlugin = definePlugin< · 42 unmodified lines
822822 resolvedSlug,
823823 config: integrationConfig satisfies OpenApiIntegrationConfig as IntegrationConfig,
824824 canRemove: true,
825− canRefresh: integrationConfig.sourceUrl != null,
825+ canRefresh: integrationConfig.specUrl != null,
826826 });
827827 if (config.healthCheck) {
828828 yield* ctx.core.integrations.setHealthCheck(slug, config.healthCheck);
@@ -871,7 +871,7 @@ export const openApiPlugin = definePlugin< · 42 unmodified lines
871871 // where the spec originally came from. A pasted-blob integration has
872872 // no origin, so updating it requires a new input.
873873 const specInput: OpenApiSpecInput | null =
874− input?.spec ?? (current.sourceUrl ? { kind: "url", url: current.sourceUrl } : null);
874+ input?.spec ?? (current.specUrl ? { kind: "url", url: current.specUrl } : null);
875875 if (specInput === null) {
876876 return yield* new OpenApiParseError({
877877 message:
@@ -911,8 +911,8 @@ export const openApiPlugin = definePlugin< · 33 unmodified lines
911911 const nextConfig: OpenApiIntegrationConfig = {
912912 ...current,
913913 specHash,
914− ...((resolved.sourceUrl ?? specInputToSourceUrl(specInput)) !== undefined
915− ? { sourceUrl: resolved.sourceUrl ?? specInputToSourceUrl(specInput) }
914+ ...((resolved.specUrl ?? specInputToSpecUrl(specInput)) !== undefined
915+ ? { specUrl: resolved.specUrl ?? specInputToSpecUrl(specInput) }
916916 : {}),
917917 };
918918
@@ -992,7 +992,7 @@ export const openApiPlugin = definePlugin< · 73 unmodified lines
992992 return yield* enrichPreviewWithDiscoveredOAuth({
993993 specText: resolved.specText,
994994 preview,
995− sourceUrl: resolved.sourceUrl ?? (spec.kind === "url" ? spec.url : undefined),
995+ specUrl: resolved.specUrl ?? (spec.kind === "url" ? spec.url : undefined),
996996 });
997997 }),
998998
@@ -1071,7 +1071,7 @@ export const openApiPlugin = definePlugin< · 72 unmodified lines
10711071 };
10721072 },
10731073
1074− staticSources: (self: OpenApiPluginExtension) => [
1074+ staticIntegrations: (self: OpenApiPluginExtension) => [
10751075 {
10761076 id: "openapi",
10771077 kind: "executor",
@@ -1104,9 +1104,9 @@ export const openApiPlugin = definePlugin< · 26 unmodified lines
11041104 requiresApproval: true,
11051105 approvalDescription: "Add an OpenAPI integration",
11061106 },
1107− inputSchema: AddSourceInputStandardSchema,
1108− outputSchema: AddSourceOutputStandardSchema,
1109− execute: (input: typeof AddSourceInputSchema.Type) =>
1107+ inputSchema: AddIntegrationInputStandardSchema,
1108+ outputSchema: AddIntegrationOutputStandardSchema,
1109+ execute: (input: typeof AddIntegrationInputSchema.Type) =>
11101110 self
11111111 .addSpec({
11121112 spec: input.spec,
packages/plugins/openapi/src/sdk/preview.ts+2−2
@@ -146,7 +146,7 @@ export const OAuth2Preset = Schema.Struct({ · 145 unmodified lines
146146 export type OAuth2Preset = typeof OAuth2Preset.Type;
147147
148148 // ---------------------------------------------------------------------------
149−// Preview operation — lightweight shape for the add-source UI list
149+// Preview operation — lightweight shape for the add-integration UI list
150150 // ---------------------------------------------------------------------------
151151
152152 export const PreviewOperation = Schema.Struct({
@@ -171,7 +171,7 @@ export const SpecPreview = Schema.Struct({ · 18 unmodified lines
171171 /** Reuses ServerInfo from extraction */
172172 servers: Schema.Array(ServerInfo),
173173 operationCount: Schema.Number,
174− /** Lightweight operation list for the add-source UI */
174+ /** Lightweight operation list for the add-integration UI */
175175 operations: Schema.Array(PreviewOperation),
176176 tags: Schema.Array(Schema.String),
177177 securitySchemes: Schema.Array(SecurityScheme),
packages/plugins/openapi/src/sdk/real-specs.test.ts+1−1
@@ -1,6 +1,6 @@
11 // Parse / extract / preview coverage against a big real-world spec.
22 // DB-touching behaviour (addSpec, removeSpec, tool registration) moved
3−// to apps/cloud/src/services/sources-api.node.test.ts — those run
3+// to the apps/cloud integrations API node tests — those run
44 // through the real Drizzle/FumaDB path so storage regressions
55 // (e.g. a per-row createMany fallback) surface automatically instead
66 // of needing a dedicated budget assertion.
packages/plugins/openapi/src/sdk/response-headers-timeout.test.ts+2−2
@@ -13,7 +13,7 @@ import { · 12 unmodified lines
1313 ToolAddress,
1414 } from "@executor-js/sdk";
1515 import { makeTestConfig, memoryCredentialsPlugin } from "@executor-js/sdk/testing";
16−import { makeOpenApiHttpApiTestSourceConfig, unwrapInvocation } from "../testing";
16+import { makeOpenApiHttpApiTestIntegrationConfig, unwrapInvocation } from "../testing";
1717
1818 import { invokeWithLayer } from "./invoke";
1919 import { openApiPlugin } from "./plugin";
@@ -46,7 +46,7 @@ const buildExecutor = (baseUrl: string) => · 26 unmodified lines
4646 Effect.gen(function* () {
4747 const executor = yield* createExecutor(makeTestConfig({ plugins: testPlugins() }));
4848 yield* executor.openapi.addSpec(
49− makeOpenApiHttpApiTestSourceConfig(TimeoutApi, { slug: "headers_timeout", baseUrl }),
49+ makeOpenApiHttpApiTestIntegrationConfig(TimeoutApi, { slug: "headers_timeout", baseUrl }),
5050 );
5151 yield* executor.connections.create({
5252 owner: "org",
packages/plugins/openapi/src/sdk/spec-blob.test.ts+2−2
@@ -34,7 +34,7 @@ import type { OpenapiStore } from "./store"; · 33 unmodified lines
3434 import type { AuthenticationInput } from "./types";
3535 import { defsBlobKey, specBlobKey } from "./store";
3636 import {
37− makeOpenApiHttpApiTestSourceConfig,
37+ makeOpenApiHttpApiTestIntegrationConfig,
3838 serveOpenApiHttpApiTestServer,
3939 unwrapInvocation,
4040 } from "../testing";
@@ -61,7 +61,7 @@ const EchoGroupLive = HttpApiBuilder.group(TestApi, "items", (handlers) => · 20 unmodified lines
6161 );
6262
6363 const specText = () => {
64− const spec = makeOpenApiHttpApiTestSourceConfig(TestApi, {}).spec;
64+ const spec = makeOpenApiHttpApiTestIntegrationConfig(TestApi, {}).spec;
6565 if (spec.kind === "blob") return spec.value;
6666 return spec.url;
6767 };
packages/plugins/openapi/src/sdk/spec-format.ts+1−1
@@ -26,7 +26,7 @@ export interface DerivedIdentity { · 25 unmodified lines
2626
2727 export interface ConvertedSpec {
2828 readonly specText: string;
29− readonly sourceUrl?: string;
29+ readonly specUrl?: string;
3030 readonly baseUrl?: string;
3131 readonly authenticationTemplate?: readonly Authentication[];
3232 readonly config?: Partial<OpenApiIntegrationConfig> & Readonly<Record<string, unknown>>;
packages/plugins/openapi/src/sdk/streaming-response.test.ts+2−2
@@ -13,7 +13,7 @@ import { · 12 unmodified lines
1313 ToolAddress,
1414 } from "@executor-js/sdk";
1515 import { makeTestConfig, memoryCredentialsPlugin } from "@executor-js/sdk/testing";
16−import { makeOpenApiHttpApiTestSourceConfig, unwrapInvocation } from "../testing";
16+import { makeOpenApiHttpApiTestIntegrationConfig, unwrapInvocation } from "../testing";
1717
1818 import { collectStreamingBody, STREAM_MAX_BYTES } from "./invoke";
1919 import { openApiPlugin } from "./plugin";
@@ -37,7 +37,7 @@ const buildExecutor = (baseUrl: string) => · 17 unmodified lines
3737 Effect.gen(function* () {
3838 const executor = yield* createExecutor(makeTestConfig({ plugins: testPlugins() }));
3939 yield* executor.openapi.addSpec(
40− makeOpenApiHttpApiTestSourceConfig(StreamingApi, { slug: "streaming", baseUrl }),
40+ makeOpenApiHttpApiTestIntegrationConfig(StreamingApi, { slug: "streaming", baseUrl }),
4141 );
4242 yield* executor.connections.create({
4343 owner: "org",
packages/plugins/openapi/src/sdk/upstream-failures.test.ts+2−2
@@ -35,7 +35,7 @@ import { · 34 unmodified lines
3535 import { makeTestConfig, memoryCredentialsPlugin } from "@executor-js/sdk/testing";
3636 import {
3737 addOpenApiTestConnection,
38− makeOpenApiHttpApiTestSourceConfig,
38+ makeOpenApiHttpApiTestIntegrationConfig,
3939 type OpenApiTestServerShape,
4040 serveOpenApiHttpApiTestServer,
4141 unwrapInvocation,
@@ -118,7 +118,7 @@ const buildExecutor = (baseUrl: string) => · 76 unmodified lines
118118 Effect.gen(function* () {
119119 const executor = yield* createExecutor(makeTestConfig({ plugins: testPlugins() }));
120120 yield* executor.openapi.addSpec(
121− makeOpenApiHttpApiTestSourceConfig(FailureApi, { slug: "f", baseUrl }),
121+ makeOpenApiHttpApiTestIntegrationConfig(FailureApi, { slug: "f", baseUrl }),
122122 );
123123 yield* executor.connections.create({
124124 owner: "org",
packages/plugins/openapi/src/testing/index.ts+20−20
@@ -92,12 +92,12 @@ export interface OpenApiEchoTestServerShape extends OpenApiTestServerShape { · 91 unmodified lines
9292 readonly clearRequests: Effect.Effect<void>;
9393 }
9494
95−export type OpenApiTestSourceOptions = Omit<OpenApiSpecConfig, "spec" | "baseUrl" | "slug"> & {
95+export type OpenApiTestIntegrationOptions = Omit<OpenApiSpecConfig, "spec" | "baseUrl" | "slug"> & {
9696 readonly baseUrl?: string | null;
9797 readonly slug?: string;
9898 };
9999
100−export type OpenApiHttpApiTestSourceOptions = Omit<
100+export type OpenApiHttpApiTestIntegrationOptions = Omit<
101101 OpenApiSpecConfig,
102102 "spec" | "slug" | "baseUrl"
103103 > & {
@@ -113,12 +113,12 @@ type OpenApiHttpApiAddSpecHeadersInput = { · 9 unmodified lines
113113 };
114114
115115 export type OpenApiHttpApiTestAddSpecPayloadOptions = Omit<
116− OpenApiHttpApiTestSourceOptions,
116+ OpenApiHttpApiTestIntegrationOptions,
117117 "headers" | "queryParams"
118118 > &
119119 OpenApiHttpApiAddSpecHeadersInput;
120120
121−export type OpenApiTestSourceExecutor = {
121+export type OpenApiTestIntegrationExecutor = {
122122 readonly openapi: Pick<OpenApiPluginExtension, "addSpec">;
123123 };
124124
@@ -160,11 +160,11 @@ export interface OpenApiTestConnection { · 35 unmodified lines
160160 export const addOpenApiTestConnection = (
161161 executor: OpenApiTestConnectionExecutor,
162162 server: OpenApiTestServerShape,
163− sourceOptions: OpenApiTestSourceOptions,
163+ integrationOptions: OpenApiTestIntegrationOptions,
164164 connectionOptions: OpenApiTestConnectionOptions = {},
165165 ): Effect.Effect<OpenApiTestConnection, unknown> =>
166166 Effect.gen(function* () {
167− const config = makeOpenApiTestSourceConfig(server, sourceOptions);
167+ const config = makeOpenApiTestIntegrationConfig(server, integrationOptions);
168168 const result = yield* executor.openapi.addSpec(config);
169169 const slug = String(result.slug);
170170 const owner: Owner = connectionOptions.owner ?? "org";
@@ -209,9 +209,9 @@ export const makeOpenApiTestSpecJson = ( · 38 unmodified lines
209209 return JSON.stringify(OpenApi.fromApi(annotated));
210210 };
211211
212−export const makeOpenApiTestSourceConfig = (
212+export const makeOpenApiTestIntegrationConfig = (
213213 server: OpenApiTestServerShape,
214− options: OpenApiTestSourceOptions,
214+ options: OpenApiTestIntegrationOptions,
215215 ): OpenApiSpecConfig => {
216216 const { baseUrl, slug, ...rest } = options;
217217 return {
@@ -222,15 +222,15 @@ export const makeOpenApiTestSourceConfig = ( · 4 unmodified lines
222222 } satisfies OpenApiSpecConfig;
223223 };
224224
225−export const addOpenApiTestSource = (
226− executor: OpenApiTestSourceExecutor,
225+export const addOpenApiTestIntegration = (
226+ executor: OpenApiTestIntegrationExecutor,
227227 server: OpenApiTestServerShape,
228− options: OpenApiTestSourceOptions,
229−) => executor.openapi.addSpec(makeOpenApiTestSourceConfig(server, options));
228+ options: OpenApiTestIntegrationOptions,
229+) => executor.openapi.addSpec(makeOpenApiTestIntegrationConfig(server, options));
230230
231−export const makeOpenApiHttpApiTestSourceConfig = (
231+export const makeOpenApiHttpApiTestIntegrationConfig = (
232232 api: HttpApi.Any,
233− options: OpenApiHttpApiTestSourceOptions,
233+ options: OpenApiHttpApiTestIntegrationOptions,
234234 ): OpenApiSpecConfig => {
235235 const { baseUrl, specBaseUrl, transformSpec, slug, ...config } = options;
236236 return {
@@ -244,18 +244,18 @@ export const makeOpenApiHttpApiTestSourceConfig = ( · 7 unmodified lines
244244 } satisfies OpenApiSpecConfig;
245245 };
246246
247−export const addOpenApiHttpApiTestSource = (
248− executor: OpenApiTestSourceExecutor,
247+export const addOpenApiHttpApiTestIntegration = (
248+ executor: OpenApiTestIntegrationExecutor,
249249 api: HttpApi.Any,
250− options: OpenApiHttpApiTestSourceOptions,
251−) => executor.openapi.addSpec(makeOpenApiHttpApiTestSourceConfig(api, options));
250+ options: OpenApiHttpApiTestIntegrationOptions,
251+) => executor.openapi.addSpec(makeOpenApiHttpApiTestIntegrationConfig(api, options));
252252
253253 export const makeOpenApiHttpApiTestAddSpecPayload = (
254254 api: HttpApi.Any,
255255 options: OpenApiHttpApiTestAddSpecPayloadOptions,
256256 ) => {
257− const { headers, queryParams, ...sourceOptions } = options;
258− const config = makeOpenApiHttpApiTestSourceConfig(api, sourceOptions);
257+ const { headers, queryParams, ...integrationOptions } = options;
258+ const config = makeOpenApiHttpApiTestIntegrationConfig(api, integrationOptions);
259259 return {
260260 spec: config.spec,
261261 slug: config.slug,
packages/plugins/provider-service-split/src/planner.test.ts+5−5
@@ -313,7 +313,7 @@ describe("provider service split migration planner", () => { · 312 unmodified lines
313313
314314 expect(planned?.config).toEqual({
315315 specHash: "mono-hash",
316− sourceUrl: "https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest",
316+ specUrl: "https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest",
317317 specFormat: "google-discovery",
318318 family: "google",
319319 authenticationTemplate: [
@@ -380,13 +380,13 @@ describe("provider service split migration planner", () => { · 60 unmodified lines
380380 const planned = plan.orgs[0]?.integrations[0];
381381
382382 expect(planned?.config).toMatchObject({
383− sourceUrl: "https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest",
383+ specUrl: "https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest",
384384 specFormat: "google-discovery",
385385 family: "google",
386386 });
387− expect(
388− (planned?.config as { readonly sourceUrl?: string } | undefined)?.sourceUrl,
389− ).not.toContain("oauth2");
387+ expect((planned?.config as { readonly specUrl?: string } | undefined)?.specUrl).not.toContain(
388+ "oauth2",
389+ );
390390 expect(planned?.servingState.operationToolNames).toEqual(["calendar.events.list"]);
391391 });
392392
packages/plugins/provider-service-split/src/planner.ts+4−4
@@ -159,7 +159,7 @@ export interface ServiceTarget { · 158 unmodified lines
159159 readonly slug: string;
160160 readonly name: string;
161161 readonly description: string;
162− readonly sourceUrl?: string;
162+ readonly specUrl?: string;
163163 readonly specFormat: "google-discovery" | "microsoft-graph";
164164 readonly authenticationTemplate?: readonly unknown[];
165165 readonly healthCheck?: unknown;
@@ -480,7 +480,7 @@ const serviceTargetForPreset = ( · 314 unmodified lines
480480 slug: preset.defaultSlug ?? preset.id,
481481 name: preset.name,
482482 description: preset.summary,
483− ...(preset.url ? { sourceUrl: preset.url } : {}),
483+ ...(preset.url ? { specUrl: preset.url } : {}),
484484 specFormat: "google-discovery",
485485 ...(preset.authTemplate ? { authenticationTemplate: preset.authTemplate } : {}),
486486 ...(preset.healthCheck ? { healthCheck: preset.healthCheck } : {}),
@@ -495,7 +495,7 @@ const serviceTargetForPreset = ( · 8 unmodified lines
495495 slug: preset.defaultSlug ?? preset.id,
496496 name: preset.name,
497497 description: preset.summary,
498− ...(preset.url ? { sourceUrl: preset.url } : {}),
498+ ...(preset.url ? { specUrl: preset.url } : {}),
499499 specFormat: "microsoft-graph",
500500 ...(preset.authTemplate ? { authenticationTemplate: preset.authTemplate } : {}),
501501 ...(preset.healthCheck ? { healthCheck: preset.healthCheck } : {}),
@@ -634,7 +634,7 @@ const configForService = (source: IntegrationRow, target: ServiceTarget): unknow · 132 unmodified lines
634634 typeof config.specHash === "string" && config.specHash.length > 0 ? config.specHash : undefined;
635635 return {
636636 ...(specHash ? { specHash } : {}),
637− ...(target.sourceUrl ? { sourceUrl: target.sourceUrl } : {}),
637+ ...(target.specUrl ? { specUrl: target.specUrl } : {}),
638638 specFormat: target.specFormat,
639639 family: target.family,
640640 ...(target.authenticationTemplate
packages/plugins/provider-service-split/src/sqlite.test.ts+1−1
@@ -207,7 +207,7 @@ describe("providerServiceSplitDataMigration", () => { · 206 unmodified lines
207207 expect(integrations.rows[0]?.plugin_id).toBe("openapi");
208208 expect(parseJson(String(integrations.rows[0]?.config))).toMatchObject({
209209 specHash: "mono-hash",
210− sourceUrl: "https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest",
210+ specUrl: "https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest",
211211 specFormat: "google-discovery",
212212 family: "google",
213213 });
packages/plugins/toolkits/src/page.tsx+11−11
@@ -198,7 +198,7 @@ type ToolkitConnectionGroup = { · 197 unmodified lines
198198
199199 type IntegrationMeta = {
200200 readonly name: string;
201− readonly sourceId: string;
201+ readonly integrationId: string;
202202 readonly icon?: string | null;
203203 readonly url?: string;
204204 };
@@ -327,17 +327,17 @@ const integrationMetaFor = ( · 122 unmodified lines
327327 const fallbackName = group.integration
328328 .replace(/[_-]/g, " ")
329329 .replace(/\b\w/g, (character) => character.toUpperCase());
330− const source = {
330+ const integrationSummary = {
331331 id: group.integration,
332332 kind: integration?.kind ?? group.integration,
333333 name: integration?.name ?? fallbackName,
334334 url: integration?.displayUrl,
335335 };
336336 return {
337− name: source.name,
338− sourceId: group.integration,
339− icon: integrationPresetIconUrl(source, integrationPlugins),
340− ...(source.url ? { url: source.url } : {}),
337+ name: integrationSummary.name,
338+ integrationId: group.integration,
339+ icon: integrationPresetIconUrl(integrationSummary, integrationPlugins),
340+ ...(integrationSummary.url ? { url: integrationSummary.url } : {}),
341341 };
342342 };
343343
@@ -346,7 +346,7 @@ type ConfiguredConnectionView = { · 2 unmodified lines
346346 readonly title: string;
347347 readonly subtitle: string;
348348 readonly pattern: string;
349− readonly sourceId: string;
349+ readonly integrationId: string;
350350 readonly icon?: string | null;
351351 readonly url?: string;
352352 };
@@ -368,7 +368,7 @@ const configuredConnectionViews = ( · 15 unmodified lines
368368 title: connection.pattern,
369369 subtitle: "Configured pattern",
370370 pattern: connection.pattern,
371− sourceId: connection.pattern.split(".")[0] ?? "toolkit",
371+ integrationId: connection.pattern.split(".")[0] ?? "toolkit",
372372 };
373373 }
374374 const meta = integrationMetaFor(group, integrations, integrationPlugins);
@@ -377,7 +377,7 @@ const configuredConnectionViews = ( · 2 unmodified lines
377377 title: connectionDisplayTitle(group, meta),
378378 subtitle: connectionDisplaySubtitleForHost(group, meta, showOwnerLabels),
379379 pattern: connection.pattern,
380− sourceId: meta.sourceId,
380+ integrationId: meta.integrationId,
381381 icon: meta.icon,
382382 ...(meta.url ? { url: meta.url } : {}),
383383 };
@@ -437,7 +437,7 @@ function ToolkitConnectionIconStack(props: { connections: readonly ConfiguredCon · 53 unmodified lines
437437 >
438438 <IntegrationFavicon
439439 icon={connection.icon}
440− sourceId={connection.sourceId}
440+ integrationId={connection.integrationId}
441441 url={connection.url}
442442 size={14}
443443 />
@@ -887,7 +887,7 @@ function AddConnectionDialog(props: { · 443 unmodified lines
887887 <span className="flex size-8 shrink-0 items-center justify-center rounded-md border border-border/70 bg-muted/30">
888888 <IntegrationFavicon
889889 icon={meta.icon}
890− sourceId={meta.sourceId}
890+ integrationId={meta.integrationId}
891891 url={meta.url}
892892 size={18}
893893 />
packages/react/src/api/error-reporting.test.ts+6−6
@@ -18,14 +18,14 @@ describe("frontend error reporting", () => { · 17 unmodified lines
1818 });
1919
2020 it("extracts stable messages from Effect exits", () => {
21− const exit = Exit.fail({ message: "Could not update source" });
21+ const exit = Exit.fail({ message: "Could not update integration" });
2222
23− expect(messageFromExit(exit, "Fallback")).toBe("Could not update source");
23+ expect(messageFromExit(exit, "Fallback")).toBe("Could not update integration");
2424 expect(messageFromExit(Exit.fail({ reason: "unknown" }), "Fallback")).toBe("Fallback");
2525 });
2626
2727 it("reports failed exits with the provided context", () => {
28− const exit = Exit.fail({ message: "Could not update source" });
28+ const exit = Exit.fail({ message: "Could not update integration" });
2929 const calls: Array<{ error: unknown; context: FrontendErrorContext }> = [];
3030
3131 reportExitFailure(
@@ -34,15 +34,15 @@ describe("frontend error reporting", () => { · 2 unmodified lines
3434 },
3535 exit,
3636 {
37− surface: "sources",
37+ surface: "integrations",
3838 action: "update",
39− message: "Could not update source",
39+ message: "Could not update integration",
4040 },
4141 );
4242
4343 expect(calls).toHaveLength(1);
4444 expect(Cause.isCause(calls[0]!.error)).toBe(true);
45− expect(calls[0]!.context.surface).toBe("sources");
45+ expect(calls[0]!.context.surface).toBe("integrations");
4646 expect(calls[0]!.context.action).toBe("update");
4747 });
4848 });
packages/react/src/components/accounts-section.tsx+1−1
@@ -397,7 +397,7 @@ export function AccountsSection(props: { · 396 unmodified lines
397397 // added widened the consent).
398398 //
399399 // Spec-derived oauth scopes are the full per-operation catalog union (e.g. an
400− // OpenAPI source like PostHog declares hundreds of scopes). Those are requested
400+ // OpenAPI integration like PostHog declares hundreds of scopes). Those are requested
401401 // broadly but not individually required: a provider that narrows the grant to
402402 // the user's actual access is healthy, not in need of reconnect. So only treat
403403 // CUSTOM (user-configured) scopes as required here; never the spec catalog.
packages/react/src/components/command-palette.tsx+7−7
@@ -23,8 +23,8 @@ import { · 22 unmodified lines
2323 // CommandPalette — global ⌘K navigator.
2424 //
2525 // Order of entries:
26−// 1. Connected sources (priority, shown first)
27−// 2. Add <Plugin> actions for each available source plugin
26+// 1. Connected integrations (priority, shown first)
27+// 2. Add <Plugin> actions for each available integration plugin
2828 // 3. Popular integrations (plugin presets)
2929 // ---------------------------------------------------------------------------
3030
@@ -46,7 +46,7 @@ export function CommandPalette(props: { open: boolean; onOpenChange: (open: bool · 15 unmodified lines
4646 return () => document.removeEventListener("keydown", onKeyDown);
4747 }, [onOpenChange, open]);
4848
49− const connectedSources = useMemo(
49+ const connectedIntegrations = useMemo(
5050 () =>
5151 AsyncResult.match(integrationsResult, {
5252 onInitial: () => [] as Array<{ id: string; name: string; kind: string; url?: string }>,
@@ -140,9 +140,9 @@ export function CommandPalette(props: { open: boolean; onOpenChange: (open: bool · 87 unmodified lines
140140 <CommandList>
141141 <CommandEmpty>No results found.</CommandEmpty>
142142
143− {connectedSources.length > 0 && (
143+ {connectedIntegrations.length > 0 && (
144144 <CommandGroup heading="Connected">
145− {connectedSources.map(
145+ {connectedIntegrations.map(
146146 (s: {
147147 readonly id: string;
148148 readonly name: string;
@@ -150,7 +150,7 @@ export function CommandPalette(props: { open: boolean; onOpenChange: (open: bool · 1 unmodified line
150150 readonly url?: string;
151151 }) => (
152152 <CommandItem
153− key={`source-${s.id}`}
153+ key={`integration-${s.id}`}
154154 value={`connected ${s.name} ${s.id} ${s.kind}`}
155155 onSelect={() => goToIntegration(s.id)}
156156 >
@@ -166,7 +166,7 @@ export function CommandPalette(props: { open: boolean; onOpenChange: (open: bool · 9 unmodified lines
166166 </CommandGroup>
167167 )}
168168
169− {connectedSources.length > 0 && integrationPlugins.length > 0 && <CommandSeparator />}
169+ {connectedIntegrations.length > 0 && integrationPlugins.length > 0 && <CommandSeparator />}
170170
171171 {integrationPlugins.length > 0 && (
172172 <CommandGroup heading="Add integration">
packages/react/src/components/integration-favicon.test.tsx+12−10
@@ -43,34 +43,36 @@ describe("IntegrationFavicon", () => { · 42 unmodified lines
4343 );
4444 });
4545
46− it("uses the Executor favicon for the built-in executor source", () => {
46+ it("uses the Executor favicon for the built-in executor integration", () => {
4747 expect(integrationLocalIconUrl("executor")).toBe("/favicon-32.png");
4848 expect(integrationLocalIconUrl("openapi")).toBeNull();
4949 });
5050
51− it("resolves the Executor sidebar icon only when the source id is threaded (cloud/self-host repro)", () => {
51+ it("resolves the Executor sidebar icon only when the integration id is threaded (cloud/self-host repro)", () => {
5252 // Reconstruct the props the multiplayer shell derives for the built-in
53− // executor source (EXECUTOR_SOURCE: kind "built-in", name "Executor", no
53+ // executor integration (EXECUTOR_INTEGRATION: kind "built-in", name "Executor", no
5454 // displayUrl). The sidebar's IntegrationList builds icon/url exactly this way.
5555 const slug = "executor";
5656 const name = "Executor";
5757 const icon = integrationPresetIconUrl({ id: slug, kind: "built-in", name, url: undefined }, []);
5858 const url = integrationInferredUrl({ id: slug, name }) ?? undefined;
5959
60− // The built-in source matches no preset and has no inferable host, so the
61− // sourceId branch is the only thing that can resolve its icon.
60+ // The built-in integration matches no preset and has no inferable host, so the
61+ // integrationId branch is the only thing that can resolve its icon.
6262 expect(icon).toBeNull();
6363 expect(url).toBeUndefined();
6464
65− // Bug: cloud/self-host dropped sourceId, so the cascade fell through to null
65+ // Bug: cloud/self-host dropped integrationId, so the cascade fell through to null
6666 // and rendered the neutral BoxIcon placeholder.
6767 expect(integrationFaviconSrc({ icon, url, size: 16 })).toBeNull();
6868
69− // Fix: threading sourceId resolves the bundled Executor favicon.
70− expect(integrationFaviconSrc({ icon, sourceId: slug, url, size: 16 })).toBe("/favicon-32.png");
69+ // Fix: threading integrationId resolves the bundled Executor favicon.
70+ expect(integrationFaviconSrc({ icon, integrationId: slug, url, size: 16 })).toBe(
71+ "/favicon-32.png",
72+ );
7173 });
7274
73− it("finds preset icons from a source URL", () => {
75+ it("finds preset icons from an integration URL", () => {
7476 expect(
7577 integrationPresetIconUrl(
7678 {
@@ -128,7 +130,7 @@ describe("IntegrationFavicon", () => { · 51 unmodified lines
128130 ).toBe("https://example.com/google.svg");
129131 });
130132
131− it("finds preset icons from a source id when the URL is missing", () => {
133+ it("finds preset icons from an integration id when the URL is missing", () => {
132134 expect(
133135 integrationPresetIconUrl(
134136 {
packages/react/src/components/integration-favicon.tsx+29−29
@@ -32,8 +32,8 @@ export function integrationFaviconFallbackUrl( · 31 unmodified lines
3232 return `https://www.google.com/s2/favicons?domain=${domain}&sz=${size * 2}`;
3333 }
3434
35−export function integrationLocalIconUrl(sourceId: string | undefined): string | null {
36− if (sourceId !== "executor") return null;
35+export function integrationLocalIconUrl(integrationId: string | undefined): string | null {
36+ if (integrationId !== "executor") return null;
3737 return "/favicon-32.png";
3838 }
3939
@@ -125,12 +125,12 @@ const tokenVariants = (value: string | undefined): readonly string[] => { · 85 unmodified lines
125125 return [...tokens];
126126 };
127127
128−export function integrationInferredUrl(source: {
128+export function integrationInferredUrl(integration: {
129129 readonly id: string;
130130 readonly name?: string;
131131 }): string | null {
132132 const hostPattern = /^[a-z0-9][a-z0-9.-]*\.(?:app|co|com|dev|io|net|org)(?:\/.*)?$/i;
133− const hostLike = [source.name, source.id.replaceAll("_", ".")]
133+ const hostLike = [integration.name, integration.id.replaceAll("_", ".")]
134134 .filter((value): value is string => value != null && value.length > 0)
135135 .find((value) => hostPattern.test(value.trim()));
136136 if (hostLike) return `https://${hostLike.trim().replace(/^https?:\/\//i, "")}`;
@@ -138,15 +138,15 @@ export function integrationInferredUrl(source: { · 1 unmodified line
138138 return null;
139139 }
140140
141−const tokenMatches = (sourceValue: string, presetValue: string): boolean =>
141+const tokenMatches = (integrationValue: string, presetValue: string): boolean =>
142142 presetValue.length > 0 &&
143− sourceValue.length > 0 &&
144− (sourceValue === presetValue ||
145− sourceValue.includes(presetValue) ||
146− presetValue.includes(sourceValue));
143+ integrationValue.length > 0 &&
144+ (integrationValue === presetValue ||
145+ integrationValue.includes(presetValue) ||
146+ presetValue.includes(integrationValue));
147147
148148 export function integrationPresetIconUrl(
149− source: {
149+ integration: {
150150 readonly id: string;
151151 readonly kind: string;
152152 readonly name?: string;
@@ -154,25 +154,25 @@ export function integrationPresetIconUrl( · 1 unmodified line
154154 },
155155 integrationPlugins: readonly IntegrationPlugin[],
156156 ): string | null {
157− const pluginKey = KIND_TO_PLUGIN_KEY[source.kind] ?? source.kind;
157+ const pluginKey = KIND_TO_PLUGIN_KEY[integration.kind] ?? integration.kind;
158158 const plugin = integrationPlugins.find((p) => p.key === pluginKey);
159159 const presets = plugin?.presets ?? [];
160− const exactSlugIcon = presets.find((p) => p.defaultSlug === source.id)?.icon;
160+ const exactSlugIcon = presets.find((p) => p.defaultSlug === integration.id)?.icon;
161161 if (exactSlugIcon) return exactSlugIcon;
162162
163− const sourceUrl = normalizeUrl(source.url);
164− const sourceGoogleService = googleApiServiceFromUrl(source.url);
165− const sourceTokens = [...tokenVariants(source.id), ...tokenVariants(source.name)];
163+ const integrationUrl = normalizeUrl(integration.url);
164+ const integrationGoogleService = googleApiServiceFromUrl(integration.url);
165+ const integrationTokens = [...tokenVariants(integration.id), ...tokenVariants(integration.name)];
166166
167167 const preset = presets.find((p) => {
168168 const presetUrl = normalizeUrl(p.url);
169169 const presetGoogleService = googleApiServiceFromUrl(p.url);
170170 const presetTokens = [...tokenVariants(p.id), ...tokenVariants(p.name)];
171171 return (
172− (sourceUrl !== null && presetUrl === sourceUrl) ||
173− (sourceGoogleService !== null && presetGoogleService === sourceGoogleService) ||
174− sourceTokens.some((sourceToken) =>
175− presetTokens.some((presetToken) => tokenMatches(sourceToken, presetToken)),
172+ (integrationUrl !== null && presetUrl === integrationUrl) ||
173+ (integrationGoogleService !== null && presetGoogleService === integrationGoogleService) ||
174+ integrationTokens.some((integrationToken) =>
175+ presetTokens.some((presetToken) => tokenMatches(integrationToken, presetToken)),
176176 )
177177 );
178178 });
@@ -181,14 +181,14 @@ export function integrationPresetIconUrl( · 2 unmodified lines
181181 }
182182
183183 // Resolution cascade for the rendered favicon: first non-null, non-failed of an
184−// explicit preset icon, the bundled local icon for a known source id, the
185−// integrations.sh logo proxy derived from the source URL, then the Google
186−// favicon service as a last resort. The built-in executor source has no preset
187−// icon and no URL, so it resolves ONLY through the sourceId branch: callers
188−// that drop sourceId fall through to the neutral BoxIcon placeholder.
184+// explicit preset icon, the bundled local icon for a known integration id, the
185+// integrations.sh logo proxy derived from the integration URL, then the Google
186+// favicon service as a last resort. The built-in executor integration has no preset
187+// icon and no URL, so it resolves ONLY through the integrationId branch: callers
188+// that drop integrationId fall through to the neutral BoxIcon placeholder.
189189 export function integrationFaviconSrc(args: {
190190 icon?: string | null;
191− sourceId?: string;
191+ integrationId?: string;
192192 url?: string;
193193 size: number;
194194 failedSrcs?: readonly string[];
@@ -197,7 +197,7 @@ export function integrationFaviconSrc(args: { · 2 unmodified lines
197197 return (
198198 [
199199 args.icon ?? null,
200− integrationLocalIconUrl(args.sourceId),
200+ integrationLocalIconUrl(args.integrationId),
201201 integrationFaviconUrl(args.url, args.size),
202202 integrationFaviconFallbackUrl(args.url, args.size),
203203 ].find((candidate) => candidate !== null && !failedSrcs.includes(candidate)) ?? null
@@ -206,17 +206,17 @@ export function integrationFaviconSrc(args: { · 2 unmodified lines
206206
207207 export function IntegrationFavicon({
208208 icon,
209− sourceId,
209+ integrationId,
210210 url,
211211 size = 16,
212212 }: {
213213 icon?: string | null;
214− sourceId?: string;
214+ integrationId?: string;
215215 url?: string;
216216 size?: number;
217217 }) {
218218 const [failedSrcs, setFailedSrcs] = useState<readonly string[]>([]);
219− const src = integrationFaviconSrc({ icon, sourceId, url, size, failedSrcs });
219+ const src = integrationFaviconSrc({ icon, integrationId, url, size, failedSrcs });
220220
221221 if (!src) {
222222 return (
packages/react/src/components/integration-icon-with-account.tsx+2−2
@@ -2,7 +2,7 @@ import { IntegrationFavicon } from "./integration-favicon"; · 1 unmodified line
22
33 export function IntegrationIconWithAccount(props: {
44 readonly icon?: string | null;
5− readonly sourceId: string;
5+ readonly integrationId: string;
66 readonly url?: string;
77 readonly size?: "sm" | "md";
88 }) {
@@ -11,7 +11,7 @@ export function IntegrationIconWithAccount(props: { · 2 unmodified lines
1111 <span className={props.size === "sm" ? "relative size-4 shrink-0" : "relative size-8 shrink-0"}>
1212 <IntegrationFavicon
1313 icon={props.icon}
14− sourceId={props.sourceId}
14+ integrationId={props.integrationId}
1515 url={props.url}
1616 size={iconSize}
1717 />
packages/react/src/components/metadata-edit-sheet.tsx+3−2
@@ -232,7 +232,8 @@ export function IntegrationEditSheet(props: { · 231 unmodified lines
232232 <SheetTitle>Edit integration</SheetTitle>
233233 <SheetDescription>
234234 The name is what people see; the description is agent-visible context — agents read it
235− when browsing sources and as fallback context on connections without one of their own.
235+ when browsing integrations and as fallback context on connections without one of their
236+ own.
236237 </SheetDescription>
237238 </SheetHeader>
238239
@@ -275,7 +276,7 @@ export function IntegrationEditSheet(props: { · 36 unmodified lines
275276
276277 {props.pluginSection ? (
277278 <Suspense fallback={null}>
278− <props.pluginSection sourceId={slug} onPendingChange={handlePendingChange} />
279+ <props.pluginSection integrationId={slug} onPendingChange={handlePendingChange} />
279280 </Suspense>
280281 ) : null}
281282 </div>
packages/react/src/lib/integration-add.tsx+1−1
@@ -1,5 +1,5 @@
11 // ---------------------------------------------------------------------------
2−// Integration add-flow helpers — the plugin-agnostic pieces every Add-source
2+// Integration add-flow helpers — the plugin-agnostic pieces every Add-integration
33 // form repeats: decoding a mutation Exit into a user-facing message, the
44 // IntegrationAlreadyExistsError guard, the live slug-collision check against
55 // the catalog, and the inline error/collision alerts.
packages/react/src/multiplayer/shell.tsx+1−1
@@ -230,7 +230,7 @@ function IntegrationList(props: { pathname: string; onNavigate?: () => void }) { · 229 unmodified lines
230230 { id: slug, kind: integration.kind, name, url: integration.displayUrl },
231231 integrationPlugins,
232232 )}
233− sourceId={slug}
233+ integrationId={slug}
234234 url={
235235 integration.displayUrl ??
236236 integrationInferredUrl({ id: slug, name }) ??
packages/react/src/pages/integration-detail.tsx+1−1
@@ -433,7 +433,7 @@ export function IntegrationDetailPage(props: { namespace: string }) { · 432 unmodified lines
433433 {editPlugin?.accounts ? (
434434 <Suspense fallback={<AccountsSkeleton />}>
435435 <editPlugin.accounts
436− sourceId={namespace}
436+ integrationId={namespace}
437437 integrationName={integrationData?.name || namespace}
438438 accountHandoff={accountHandoff}
439439 />
packages/react/src/pages/integrations.tsx+2−2
@@ -458,7 +458,7 @@ function IntegrationGrid(props: { integrations: readonly Integration[] }) { · 457 unmodified lines
458458 { id: slug, kind: integration.kind, name, url: integration.displayUrl },
459459 integrationPlugins,
460460 )}
461− sourceId={slug}
461+ integrationId={slug}
462462 url={integration.displayUrl ?? integrationInferredUrl({ id: slug, name }) ?? undefined}
463463 />
464464 <CardStackEntryContent>
@@ -468,7 +468,7 @@ function IntegrationGrid(props: { integrations: readonly Integration[] }) { · 3 unmodified lines
468468 <CardStackEntryActions>
469469 {SummaryComponent && (
470470 <Suspense fallback={null}>
471− <SummaryComponent sourceId={slug} />
471+ <SummaryComponent integrationId={slug} />
472472 </Suspense>
473473 )}
474474 <IntegrationHealthSummary integration={integration.slug} />
packages/react/src/plugins/integration-identity.tsx+7−7
@@ -40,10 +40,10 @@ export function pascalCaseDomainLabel(label: string): string | null { · 39 unmodified lines
4040 .join("");
4141 }
4242
43−export function integrationDisplayNameFromUrl(url: string, sourceKind: string): string | null {
43+export function integrationDisplayNameFromUrl(url: string, integrationKind: string): string | null {
4444 const label = domainLabelFromUrl(url);
4545 const displayLabel = label ? pascalCaseDomainLabel(label) : null;
46− return displayLabel ? `${displayLabel} ${sourceKind}` : null;
46+ return displayLabel ? `${displayLabel} ${integrationKind}` : null;
4747 }
4848
4949 // Package runners whose own name is never the integration name: the meaningful
@@ -129,11 +129,11 @@ export function humanizeStdioToken(token: string): string | null { · 79 unmodified lines
129129 export function integrationDisplayNameFromStdio(
130130 command: string,
131131 args: readonly string[],
132− sourceKind: string,
132+ integrationKind: string,
133133 ): string | null {
134134 const token = stdioPackageToken(command, args);
135135 const label = token ? humanizeStdioToken(token) : null;
136− return label ? `${label} ${sourceKind}` : null;
136+ return label ? `${label} ${integrationKind}` : null;
137137 }
138138
139139 // ---------------------------------------------------------------------------
@@ -208,7 +208,7 @@ export interface IntegrationIdentityFieldsProps { · 68 unmodified lines
208208 readonly namespaceHint?: string;
209209 /**
210210 * When true, the namespace field is rendered disabled — useful on Edit
211− * forms, where the namespace is the source's identity and changing it
211+ * forms, where the namespace is the integration's identity and changing it
212212 * would require a delete + recreate flow.
213213 */
214214 readonly namespaceReadOnly?: boolean;
@@ -225,7 +225,7 @@ export function IntegrationIdentityFields({ · 10 unmodified lines
225225 const effectiveNamespaceHint =
226226 namespaceHint ??
227227 (namespaceReadOnly
228− ? "The namespace is part of the source's identity and cannot be changed."
228+ ? "The namespace is part of the integration's identity and cannot be changed."
229229 : undefined);
230230
231231 return (
@@ -255,7 +255,7 @@ export function IntegrationIdentityFieldRows({ · 23 unmodified lines
255255 const effectiveNamespaceHint =
256256 namespaceHint ??
257257 (namespaceReadOnly
258− ? "The namespace is part of the source's identity and cannot be changed."
258+ ? "The namespace is part of the integration's identity and cannot be changed."
259259 : undefined);
260260
261261 return (
packages/react/src/plugins/oauth-reconnect.test.ts+1−1
@@ -117,7 +117,7 @@ describe("missingScopes (Part 2 informational subset warning)", () => { · 116 unmodified lines
117117
118118 describe("reconsentRequiredScopes", () => {
119119 it("treats spec-derived oauth scopes as NOT required (opportunistic catalog)", () => {
120− // An OpenAPI source (e.g. PostHog) declares the full per-operation scope
120+ // An OpenAPI integration (e.g. PostHog) declares the full per-operation scope
121121 // catalog. A narrower grant is healthy and must not nag for reconnect.
122122 expect(
123123 reconsentRequiredScopes({
packages/react/src/plugins/oauth-reconnect.ts+1−1
@@ -113,7 +113,7 @@ export function connectionNeedsReconsent( · 112 unmodified lines
113113 * granted, given the integration's oauth auth method.
114114 *
115115 * Spec-derived oauth scopes are the full per-operation catalog union (an OpenAPI
116− * source like PostHog declares hundreds): requested broadly to unlock as many
116+ * integration like PostHog declares hundreds): requested broadly to unlock as many
117117 * tools as possible, but none individually required. A provider that narrows the
118118 * grant to the user's actual access is healthy, so the spec catalog must NOT
119119 * drive a reconnect prompt. Custom (user-configured) scopes are intentional and
scripts/agent-config-smoke.ts+5−5
@@ -268,10 +268,10 @@ const runSmoke = async (oauth: OAuthTestServerShape, graph: GraphqlTestServerSha · 267 unmodified lines
268268 `OAuth connection was not listed: ${JSON.stringify(connections)}`,
269269 );
270270
271− console.log("[agent-config-smoke] add OAuth-backed GraphQL source through approval flow");
272− const addSource = await approvePausedCall(
271+ console.log("[agent-config-smoke] add OAuth-backed GraphQL integration through approval flow");
272+ const addIntegration = await approvePausedCall(
273273 ctx,
274− await callTool(ctx, ["executor", "graphql", "addSource"], {
274+ await callTool(ctx, ["executor", "graphql", "addIntegration"], {
275275 scope: ctx.scopeDir,
276276 endpoint: graph.endpoint,
277277 name: "Agent Smoke GraphQL",
@@ -297,8 +297,8 @@ const runSmoke = async (oauth: OAuthTestServerShape, graph: GraphqlTestServerSha · 19 unmodified lines
297297 },
298298 }),
299299 );
300− const added = parseJsonOutput<{ readonly ok: boolean }>(addSource);
301− assert(added.ok === true, `GraphQL addSource failed: ${addSource.text}`);
300+ const added = parseJsonOutput<{ readonly ok: boolean }>(addIntegration);
301+ assert(added.ok === true, `GraphQL addIntegration failed: ${addIntegration.text}`);
302302
303303 console.log("[agent-config-smoke] invoke configured OAuth-backed tool");
304304 const invoked = parseJsonOutput<{ readonly ok: boolean; readonly data?: unknown }>(
tests/presets-reachable.test.ts+1−1
@@ -176,7 +176,7 @@ describe("public preset URLs are detected by the correct plugin", () => { · 175 unmodified lines
176176 () =>
177177 Effect.gen(function* () {
178178 const executor = yield* makeExecutor();
179− const results = yield* executor.sources.detect(preset.url);
179+ const results = yield* executor.integrations.detect(preset.url);
180180
181181 expect(
182182 results.length,
tests/tools-cli.test.ts+5−5
@@ -6,7 +6,7 @@ import { · 5 unmodified lines
66 buildToolPath,
77 filterToolPathChildren,
88 buildInvokeToolCode,
9− buildListSourcesCode,
9+ buildListIntegrationsCode,
1010 buildSearchToolsCode,
1111 extractPausedInteraction,
1212 extractExecutionId,
@@ -54,19 +54,19 @@ describe("CLI tooling helpers", () => { · 41 unmodified lines
5454 expect(() => buildToolPath(["github", "issues", "create now"])).toThrow();
5555 });
5656
57− it("builds search and sources code snippets", () => {
57+ it("builds search and integrations code snippets", () => {
5858 const searchCode = buildSearchToolsCode({
5959 query: "google calendar events",
6060 namespace: "google",
6161 limit: 5,
6262 });
63− const sourcesCode = buildListSourcesCode({ query: "google", limit: 20 });
63+ const integrationsCode = buildListIntegrationsCode({ query: "google", limit: 20 });
6464
6565 expect(searchCode).toBe(
6666 'return await tools.search({"query":"google calendar events","limit":5,"namespace":"google"});',
6767 );
68− expect(sourcesCode).toBe(
69− 'return await tools.executor.sources.list({"limit":20,"query":"google"});',
68+ expect(integrationsCode).toBe(
69+ 'return await tools.executor.integrations.list({"limit":20,"query":"google"});',
7070 );
7171 });
7272
vision.md+25−25
@@ -14,7 +14,7 @@ it safely. · 13 unmodified lines
1414 - It is **not AI-specific.** Agents are a primary consumer, but the layer is a general way to
1515 represent and interop between sources of capability.
1616 - It is **not code-mode-specific.** Code mode is one way to call tools; it isn't the point.
17−- It is a category of **source** and a way to **interop between them** through one set of
17+- It is a category of **integration** and a way to **interop between them** through one set of
1818 concepts.
1919
2020 The bet: as work shifts toward agents acting through software, every company needs one layer
@@ -30,23 +30,23 @@ multiplayer must be powerful; neither carries the other's weight. · 9 unmodified lines
3030
3131 - **Tool** — an id, an optional input schema, and an optional output schema (JSON Schema today;
3232 may grow richer types). The unit of capability. Addressed as
33− `<source>.<scope>.<connection>.<tool>`.
34−- **Source** — contains tools. A source is produced by a plugin from some config (an OpenAPI
33+ `<integration>.<scope>.<connection>.<tool>`.
34+- **Integration** — contains tools. An integration is produced by a plugin from some config (an OpenAPI
3535 spec, a GraphQL endpoint, an MCP server, a CLI's install/run). The core never sees the raw
3636 config — only a normalized manifest.
37−- **Connection** — a credential, born wired, identified by `(scope, source, name)`. The `name`
38− is required and load-bearing (the account: `work`, `personal`, `prod`). One source holds many
37+- **Connection** — a credential, born wired, identified by `(scope, integration, name)`. The `name`
38+ is required and load-bearing (the account: `work`, `personal`, `prod`). One integration holds many
3939 connections.
4040 - **Secret** — never lives in Executor and never reaches the agent. A connection holds a
4141 `SecretRef` (a pointer — `op://`, `keychain://`, `env://`, `vault://`); a provider resolves it
4242 at call time, in trusted space, behind a proxy.
4343 - **Scope** — placement and identity. An ordered, merged set (see _Scopes_). Every record is
4444 placed in a scope.
45−- **Policy** — gates execution (allow / require-approval / block). Attached to sources and
45+- **Policy** — gates execution (allow / require-approval / block). Attached to integrations and
4646 connections (see _Policies_).
47−- **Plugin** — registers sources, tools, providers, storage, surfaces. The one open extension
47+- **Plugin** — registers integrations, tools, providers, storage, surfaces. The one open extension
4848 seam (see _The model_).
49−- **Manager / Invoker** — the core runtime roles: a manager owns a source's tools and config; an
49+- **Manager / Invoker** — the core runtime roles: a manager owns an integration's tools and config; an
5050 invoker executes a tool through the right connection and proxy.
5151
5252 ## The model — core, first-party, one seam, surfaces
@@ -63,12 +63,12 @@ generic plugin registry: toolkits, the MCP host, generative UI, workflows, stora · 10 unmodified lines
6363 audit/runs, the internal-apps catalog. They're separable but they don't go through an abstract
6464 plugin contract, because nobody outside you needs to add a new _kind_ of them.
6565
66−**One open plugin seam: sources.** There are thousands of APIs; you'll never enumerate them; you
66+**One open plugin seam: integrations.** There are thousands of APIs; you'll never enumerate them; you
6767 want others to add them. That is where an open contract (`resolveTools` / `invokeTool`) earns its
6868 complexity. Secret **providers** are the one plausible second seam, for the same reason.
6969
7070 > The discipline: do not make "everything a plugin." Keep first-party capabilities first-party,
71−> and keep exactly one open seam (sources, + maybe providers). Reversible — extract a new seam
71+> and keep exactly one open seam (integrations, + maybe providers). Reversible — extract a new seam
7272 > only when several first-party capabilities demand the same one.
7373
7474 **Surfaces / hosts** serve the executor over a wire: MCP, HTTP, CLI, stdio, triggers, the web
@@ -128,11 +128,11 @@ is to _attach_ a record to a scope. · 53 unmodified lines
128128 ## Connections, secrets & auth
129129
130130 - **Connection = a credential, born wired** (secret + account fused; no slot/binding split),
131− identified by `(scope, source, name)` with `name` required.
132−- **Multiple accounts per source per scope** (distinct names). Per-member auth (each connects
131+ identified by `(scope, integration, name)` with `name` required.
132+- **Multiple accounts per integration per scope** (distinct names). Per-member auth (each connects
133133 their own) and team-level shared auth (one credential placed in an outer scope) are both just
134134 placement.
135−- **Auth template vs credential.** A source declares _how_ it authenticates (OAuth / bearer /
135+- **Auth template vs credential.** An integration declares _how_ it authenticates (OAuth / bearer /
136136 API key — a discriminated set); each connection fills that template.
137137 - **Secrets never reach the agent.** Resolved by a provider at call time, in trusted space,
138138 behind a **tool-proxy** — credentials never enter agent or sandbox code. **No escape hatch:** a
@@ -141,7 +141,7 @@ is to _attach_ a record to a scope. · 2 unmodified lines
141141 separately. **Credential lifecycle** (refresh, rotation, revocation, expiry) handled below the
142142 agent.
143143
144−## Sources & source kinds
144+## Integrations & integration kinds
145145
146146 Each kind is added through the **one open seam** (`resolveTools` + `invokeTool`): **OpenAPI**,
147147 **GraphQL**, **MCP servers**, **CLI** (install + run, no spec), **direct API / raw-fetch**, and
@@ -150,25 +150,25 @@ reasons over a **normalized manifest** (schemas, side-effect class, auth templat · 2 unmodified lines
150150 required capabilities, an LLM-authored description). **Spec auto-refresh** tracks upstream drift
151151 and **must never auto-expand authority** — new operations appear _ungranted until reviewed_.
152152
153−An **integrations registry** lets people discover and add sources.
153+An **integrations registry** lets people discover and add integrations.
154154
155155 ## Policies
156156
157157 Policies gate **execution** — `approve` / `require_approval` / `block` — and are distinct from
158158 toolkits (which gate visibility). Resolution is _most-restrictive-wins_; **plugin defaults** let
159−a source ship a tool pre-marked `require_approval` (destructive detection), with `approve` as the
159+an integration ship a tool pre-marked `require_approval` (destructive detection), with `approve` as the
160160 override when an import wrongly flags a safe action.
161161
162162 How people actually use them today (real usage): glob patterns over the address, where _every_
163−rule targets a **source** and wildcards the scope/connection (`stripe_api.*.*.disputes.*`,
163+rule targets an **integration** and wildcards the scope/connection (`stripe_api.*.*.disputes.*`,
164164 `pscale.*.*.execute_write_query`) — i.e. faking structural targeting because there's nowhere to
165165 attach it. The direction:
166166
167−- **Kill the global glob list. Attach policy to the source and connection records** — the two
168− structural levels. **Connection overrides source** (innermost-wins), with **scope authority
167+- **Kill the global glob list. Attach policy to the integration and connection records** — the two
168+ structural levels. **Connection overrides integration** (innermost-wins), with **scope authority
169169 layered on top** (outer/org mandatory, can't be weakened inward).
170−- Fixing a mis-flagged tool becomes `approve` _on that source/connection_, directly.
171−- **Toolkits carry no policy** — they're pure curation and inherit whatever source/connection
170+- Fixing a mis-flagged tool becomes `approve` _on that integration/connection_, directly.
171+- **Toolkits carry no policy** — they're pure curation and inherit whatever integration/connection
172172 policies apply to the tools they expose. This dissolves the old global-vs-toolkit merge
173173 conflict.
174174
@@ -230,7 +230,7 @@ You build once; surfaces project. · 55 unmodified lines
230230 draw on, that **surfaces in tool search**, and can **run tools inline** to cut round-trips.
231231 Served off MCP and from a `.well-known`. File-backed (see _Authoring model_).
232232 - **Internal apps catalog & custom UI snippets.** Store and share custom UI and internal apps.
233−- **Configure Executor via Executor** — managing sources, connections, scopes, policies,
233+- **Configure Executor via Executor** — managing integrations, connections, scopes, policies,
234234 toolkits is itself done through tools.
235235
236236 ## Authoring model — two paths
@@ -309,7 +309,7 @@ service install` (daemon), `executor web` (web app) — one instance, delivered · 72 unmodified lines
309309 The hosted product runs on a **Cloudflare substrate** and lets users **write plugins and load
310310 them dynamically, sandboxed** — each in a Worker isolate with a **declared capability manifest**
311311 (a plugin asking for specific scopes can do exactly that and nothing else). User plugins ride the
312−**source seam** and the capability membrane, opened **last and curated**. Local == remote via
312+**integration seam** and the capability membrane, opened **last and curated**. Local == remote via
313313 **substrate substitution**: a local V8 isolate stands in for the dynamic worker, Git for
314314 Cloudflare Artifacts, local SQLite for hosted storage.
315315
@@ -329,7 +329,7 @@ Cloudflare Artifacts, local SQLite for hosted storage. · 13 unmodified lines
329329 Built on **Effect**. The same SDK is in-memory _and_ client/server; SDK-only users need no
330330 server; serving is a standard Web `Request → Response` handler. The discipline that keeps breadth
331331 cheap is the **seam discipline**: the platform owns generic concerns (auth, caching, retries,
332−schema, rendering, transport, the Run); a plugin owns only the source-specific resolve/invoke.
332+schema, rendering, transport, the Run); a plugin owns only the integration-specific resolve/invoke.
333333
334334 ## Principles
335335
@@ -356,7 +356,7 @@ schema, rendering, transport, the Run); a plugin owns only the source-specific r · 20 unmodified lines
356356 nearest-to-substrate first: MCP → storage → workflows → generative UI → user plugins (curated,
357357 last). Three substrate invariants keep composition cheap: one typed invocation primitive;
358358 authorization inherited from the substrate, never re-implemented; a hard seam between
359− substrate-generic and source-specific code.
359+ substrate-generic and integration-specific code.
360360 - **Vision mode vs build mode.** This document is vision mode — generativity ("and then you'd
361361 want X") is the point. In **build mode, every "and then you'd want X" is a YAGNI to defer**, not
362362 a dependency to satisfy. Build the smallest thing useful on its own; let the next be _pulled_ by