History

Add new linting rules for OpenAPI bridge and documents; update workspace references to scope; refactor workspace-related identifiers to scope in various files

Rhys Sullivan committed 7 months ago

Commit d05aa9d1 parent 22bc077153 files · +4493 −2886Browse files

Unified · Split

153 files
  1. .executor/executor.jsonc +105 −10
  2. .oxlintrc.jsonc +13 −0
  3. .tmp-openapi-helper-probe.ts +30 −0
  4. CLAUDE.md +7 −0
  5. apps/executor/package.json +1 −0
  6. apps/executor/src/cli/dev.ts +3 −3
  7. apps/executor/src/cli/main.ts +15 −11
  8. apps/executor/src/distribution/distribution.test.ts +6 −6
  9. apps/executor/src/server/server.real-ingestion.test.ts +11 −11
  10. apps/executor/src/server/server.test.ts +111 −181
  11. apps/web/src/views/add-source.tsx +2 −2
  12. bun.lock +19 −0
  13. examples/sqlite-sdk-consumer/src/main.ts +2 −2
  14. examples/sqlite-sdk-consumer/src/sqlite-backend.ts +84 −81
  15. packages/clients/react/package.json +1 −0
  16. packages/clients/react/src/index.test.tsx +46 −107
  17. packages/clients/react/src/index.ts +21 −21
  18. packages/dev/effect-test-utils/package.json +22 −0
  19. packages/dev/effect-test-utils/src/index.ts +103 −0
  20. packages/dev/effect-test-utils/tsconfig.json +15 −0
  21. packages/hosts/mcp/src/index.ts +24 −24
  22. packages/hosts/mcp/src/paused-result.test.ts +2 −2
  23. packages/platform/api/src/executions/api.ts +1 −1
  24. packages/platform/api/src/executor-context.ts +3 −3
  25. packages/platform/api/src/oauth/api.ts +1 −1
  26. packages/platform/api/src/oauth/http.ts +1 −1
  27. packages/platform/api/src/policies/api.ts +2 −2
  28. packages/platform/api/src/sources/api.ts +6 −6
  29. packages/platform/api/src/sources/http.ts +6 −6
  30. packages/platform/internal/src/tools/executor-admin.ts +103 −103
  31. packages/platform/sdk-file/src/config.test.ts +1 −1
  32. packages/platform/sdk-file/src/config.ts +5 −5
  33. packages/platform/sdk-file/src/executor-state-store.test.ts +1 −1
  34. packages/platform/sdk-file/src/executor-state-store.ts +95 −95
  35. packages/platform/sdk-file/src/index.ts +8 −8
  36. packages/platform/sdk-file/src/installation.test.ts +4 −4
  37. packages/platform/sdk-file/src/installation.ts +9 −6
  38. packages/platform/sdk-file/src/secret-material-providers.ts +7 −7
  39. packages/platform/sdk-file/src/source-artifacts.test.ts +2 −2
  40. packages/platform/sdk-file/src/source-type-declarations.test.ts +2 −3
  41. packages/platform/sdk-file/src/workspace-state.ts +9 −9
  42. packages/platform/sdk/package.json +1 −0
  43. packages/platform/sdk/src/backend.ts +14 −14
  44. packages/platform/sdk/src/errors.ts +3 −1
  45. packages/platform/sdk/src/executions/contracts.ts +3 −1
  46. packages/platform/sdk/src/executor.test.ts +453 −0
  47. packages/platform/sdk/src/executor.ts +95 −78
  48. packages/platform/sdk/src/local/contracts.ts +3 −1
  49. packages/platform/sdk/src/local/operations.ts +32 −20
  50. packages/platform/sdk/src/local/secrets.ts +18 −10
  51. packages/platform/sdk/src/policies/contracts.ts +10 −8
  52. packages/platform/sdk/src/policies/operations.ts +135 −127
  53. packages/platform/sdk/src/runtime/auth/auth-artifacts.ts +19 −15
  54. packages/platform/sdk/src/runtime/auth/auth-leases.ts +30 −17
  55. packages/platform/sdk/src/runtime/auth/mcp-auth-provider.ts +4 −2
  56. packages/platform/sdk/src/runtime/auth/oauth-loopback.test.ts +11 −3
  57. packages/platform/sdk/src/runtime/auth/oauth-loopback.ts +3 −1
  58. packages/platform/sdk/src/runtime/auth/provider-grant-lifecycle.ts +9 −7
  59. packages/platform/sdk/src/runtime/auth/source-auth-material.test.ts +14 −8
  60. packages/platform/sdk/src/runtime/auth/source-auth-material.ts +20 −13
  61. packages/platform/sdk/src/runtime/catalog/catalog-typescript.ts +9 −3
  62. packages/platform/sdk/src/runtime/catalog/prettier-format.ts +3 −1
  63. packages/platform/sdk/src/runtime/catalog/source/reconcile.ts +24 −15
  64. packages/platform/sdk/src/runtime/catalog/source/runtime.test.ts +12 −5
  65. packages/platform/sdk/src/runtime/catalog/source/runtime.ts +65 −56
  66. packages/platform/sdk/src/runtime/catalog/source/snapshot.test.ts +21 −7
  67. packages/platform/sdk/src/runtime/catalog/source/sync.ts +53 −45
  68. packages/platform/sdk/src/runtime/catalog/source/type-declarations.ts +6 −2
  69. packages/platform/sdk/src/runtime/execution/http.test.ts +39 −19
  70. packages/platform/sdk/src/runtime/execution/ir-execution.ts +21 −8
  71. packages/platform/sdk/src/runtime/execution/live.test.ts +8 −2
  72. packages/platform/sdk/src/runtime/execution/live.ts +6 −2
  73. packages/platform/sdk/src/runtime/execution/mcp-resume.test.ts +65 −27
  74. packages/platform/sdk/src/runtime/execution/runtime-config.test.ts +36 −20
  75. packages/platform/sdk/src/runtime/execution/runtime.ts +16 −5
  76. packages/platform/sdk/src/runtime/execution/scope/authorization.ts +33 −16
  77. packages/platform/sdk/src/runtime/execution/scope/environment.ts +55 −37
  78. packages/platform/sdk/src/runtime/execution/scope/local.ts +12 −0
  79. packages/platform/sdk/src/runtime/execution/scope/source-catalog.ts +45 −38
  80. packages/platform/sdk/src/runtime/execution/scope/tool-invoker.ts +92 −65
  81. packages/platform/sdk/src/runtime/execution/service.ts +35 −30
  82. packages/platform/sdk/src/runtime/execution/state.ts +6 −3
  83. packages/platform/sdk/src/runtime/execution/test-http-client.ts +4 −2
  84. packages/platform/sdk/src/runtime/execution/workspace/local.ts +0 −12
  85. packages/platform/sdk/src/runtime/executor-runtime.test.ts +195 −195
  86. packages/platform/sdk/src/runtime/executor-state-store.ts +46 −46
  87. packages/platform/sdk/src/runtime/index.ts +110 −72
  88. packages/platform/sdk/src/runtime/policy/invocation-policy-engine.test.ts +18 −12
  89. packages/platform/sdk/src/runtime/policy/invocation-policy-engine.ts +7 −7
  90. packages/platform/sdk/src/runtime/scope-config.ts +3 −1
  91. packages/platform/sdk/src/runtime/scope-errors.ts +6 −6
  92. packages/platform/sdk/src/runtime/scope-state.ts +6 −6
  93. packages/platform/sdk/src/runtime/scope/config-secrets.ts +0 −0
  94. packages/platform/sdk/src/runtime/scope/runtime-context.ts +85 −0
  95. packages/platform/sdk/src/runtime/scope/scope-sync.ts +16 −10
  96. packages/platform/sdk/src/runtime/scope/secret-material-providers.ts +3 −1
  97. packages/platform/sdk/src/runtime/scope/storage.ts +43 −30
  98. packages/platform/sdk/src/runtime/source-artifacts.ts +3 −1
  99. packages/platform/sdk/src/runtime/sources/executor-tools.ts +67 −60
  100. packages/platform/sdk/src/runtime/sources/graphql-tools.test.ts +13 −3
  101. packages/platform/sdk/src/runtime/sources/source-adapter-fixture-matrix.test.ts +47 −23
  102. packages/platform/sdk/src/runtime/sources/source-adapters/google-discovery.test.ts +24 −8
  103. packages/platform/sdk/src/runtime/sources/source-adapters/graphql.test.ts +24 −8
  104. packages/platform/sdk/src/runtime/sources/source-adapters/index.ts +6 −2
  105. packages/platform/sdk/src/runtime/sources/source-adapters/internal.ts +3 −1
  106. packages/platform/sdk/src/runtime/sources/source-adapters/mcp.test.ts +73 −34
  107. packages/platform/sdk/src/runtime/sources/source-auth-service.ts +379 −305
  108. packages/platform/sdk/src/runtime/sources/source-credential-interactions.ts +4 −1
  109. packages/platform/sdk/src/runtime/sources/source-definitions.test.ts +20 −14
  110. packages/platform/sdk/src/runtime/sources/source-definitions.ts +20 −14
  111. packages/platform/sdk/src/runtime/sources/source-discovery.test.ts +8 −2
  112. packages/platform/sdk/src/runtime/sources/source-inspection.test.ts +11 −3
  113. packages/platform/sdk/src/runtime/sources/source-store.test.ts +20 −10
  114. packages/platform/sdk/src/runtime/sources/source-store.ts +60 −52
  115. packages/platform/sdk/src/runtime/sources/source-store/auth.ts +27 −18
  116. packages/platform/sdk/src/runtime/sources/source-store/config.ts +16 −6
  117. packages/platform/sdk/src/runtime/sources/source-store/deps.ts +60 −52
  118. packages/platform/sdk/src/runtime/sources/source-store/lifecycle.ts +69 −60
  119. packages/platform/sdk/src/runtime/sources/source-store/records.ts +76 −65
  120. packages/platform/sdk/src/runtime/workspace/runtime-context.ts +0 −78
  121. packages/platform/sdk/src/schema/common.ts +3 −1
  122. packages/platform/sdk/src/schema/enums.ts +4 −4
  123. packages/platform/sdk/src/schema/ids.ts +11 −11
  124. packages/platform/sdk/src/schema/index.ts +1 −1
  125. packages/platform/sdk/src/schema/models/auth-artifact.ts +9 −6
  126. packages/platform/sdk/src/schema/models/auth-lease.ts +9 −6
  127. packages/platform/sdk/src/schema/models/code-migration.ts +6 −2
  128. packages/platform/sdk/src/schema/models/execution.ts +9 −6
  129. packages/platform/sdk/src/schema/models/local-config.ts +6 −2
  130. packages/platform/sdk/src/schema/models/local-installation.ts +4 −4
  131. packages/platform/sdk/src/schema/models/policy.ts +22 −15
  132. packages/platform/sdk/src/schema/models/provider-auth-grant.ts +11 −8
  133. packages/platform/sdk/src/schema/models/scope-oauth-client.ts +12 −8
  134. packages/platform/sdk/src/schema/models/secret-material.ts +9 −3
  135. packages/platform/sdk/src/schema/models/source-auth-session.ts +11 −8
  136. packages/platform/sdk/src/schema/models/source-catalog.ts +7 −3
  137. packages/platform/sdk/src/schema/models/source-inspection.ts +6 −2
  138. packages/platform/sdk/src/schema/models/source-oauth-client.ts +22 −18
  139. packages/platform/sdk/src/schema/models/source.ts +18 −10
  140. packages/platform/sdk/src/schema/schema.test.ts +5 −1
  141. packages/platform/sdk/src/scope.ts +17 −0
  142. packages/platform/sdk/src/sources/contracts.ts +11 −11
  143. packages/platform/sdk/src/sources/discovery.ts +3 −1
  144. packages/platform/sdk/src/sources/inspection.ts +25 −19
  145. packages/platform/sdk/src/sources/operations.ts +43 −34
  146. packages/platform/sdk/src/workspace.ts +0 −15
  147. packages/sources/core/src/source-models.ts +6 −6
  148. packages/sources/google-discovery/src/adapter.test.ts +1 −1
  149. packages/sources/google-discovery/src/adapter.ts +3 −3
  150. tools/oxlint/README.md +2 −0
  151. tools/oxlint/plugin.mjs +4 −0
  152. tools/oxlint/rules/no-manual-httpapi-openapi-bridge.mjs +65 −0
  153. tools/oxlint/rules/no-manual-openapi-documents.mjs +108 −0
.executor/executor.jsonc+105−10
@@ -1,22 +1,117 @@
11 {
22 "sources": {
33 "github": {
4− "kind": "openapi",
54 "name": "GitHub",
65 "connection": {
7− "endpoint": "https://api.github.com/"
6+ "endpoint": "https://api.github.com"
87 },
8+ "kind": "openapi",
99 "binding": {
10− "specUrl": "https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/api.github.com/api.github.com.json",
10+ "specUrl": "https://example.com/github-openapi.yaml",
1111 "defaultHeaders": null
1212 }
13− }
14− },
15− "policies": {
16− "demo.*": {
17− "match": "demo.*",
18− "action": "allow",
19− "approval": "auto"
13+ },
14+ "googleapis": {
15+ "name": "Drive",
16+ "namespace": "google.drive",
17+ "connection": {
18+ "endpoint": "http://127.0.0.1:60144/drive/$discovery/rest?version=v3"
19+ },
20+ "kind": "google_discovery",
21+ "binding": {
22+ "service": "drive",
23+ "version": "v3",
24+ "discoveryUrl": "http://127.0.0.1:60144/drive/$discovery/rest?version=v3",
25+ "defaultHeaders": null,
26+ "scopes": [
27+ "scope:drive.readonly"
28+ ]
29+ }
30+ },
31+ "google-gmail": {
32+ "name": "Gmail",
33+ "namespace": "google.gmail",
34+ "connection": {
35+ "endpoint": "http://127.0.0.1:60197/gmail/$discovery/rest?version=v1"
36+ },
37+ "kind": "google_discovery",
38+ "binding": {
39+ "service": "gmail",
40+ "version": "v1",
41+ "discoveryUrl": "http://127.0.0.1:60197/gmail/$discovery/rest?version=v1",
42+ "defaultHeaders": null,
43+ "scopes": [
44+ "scope:gmail.readonly"
45+ ]
46+ }
47+ },
48+ "google-calendar": {
49+ "name": "Calendar",
50+ "namespace": "google.calendar",
51+ "connection": {
52+ "endpoint": "http://127.0.0.1:60190/calendar/$discovery/rest?version=v3"
53+ },
54+ "kind": "google_discovery",
55+ "binding": {
56+ "service": "calendar",
57+ "version": "v3",
58+ "discoveryUrl": "http://127.0.0.1:60190/calendar/$discovery/rest?version=v3",
59+ "defaultHeaders": null,
60+ "scopes": [
61+ "scope:calendar.readonly"
62+ ]
63+ }
64+ },
65+ "google-drive": {
66+ "name": "Drive",
67+ "namespace": "google.drive",
68+ "connection": {
69+ "endpoint": "http://127.0.0.1:60205/drive/$discovery/rest?version=v3"
70+ },
71+ "kind": "google_discovery",
72+ "binding": {
73+ "service": "drive",
74+ "version": "v3",
75+ "discoveryUrl": "http://127.0.0.1:60205/drive/$discovery/rest?version=v3",
76+ "defaultHeaders": null,
77+ "scopes": [
78+ "scope:drive.readonly"
79+ ]
80+ }
81+ },
82+ "google-drive-2": {
83+ "name": "Drive",
84+ "namespace": "google.drive",
85+ "connection": {
86+ "endpoint": "https://example.test/drive/$discovery/rest?version=v3"
87+ },
88+ "kind": "google_discovery",
89+ "binding": {
90+ "service": "drive",
91+ "version": "v3",
92+ "discoveryUrl": "https://example.test/drive/$discovery/rest?version=v3",
93+ "defaultHeaders": null,
94+ "scopes": [
95+ "scope:drive.readonly"
96+ ]
97+ }
98+ },
99+ "google-gmail-2": {
100+ "kind": "google_discovery",
101+ "name": "Gmail",
102+ "namespace": "google.gmail",
103+ "connection": {
104+ "endpoint": "https://example.test/gmail/$discovery/rest?version=v1"
105+ },
106+ "binding": {
107+ "service": "gmail",
108+ "version": "v1",
109+ "discoveryUrl": "https://example.test/gmail/$discovery/rest?version=v1",
110+ "defaultHeaders": null,
111+ "scopes": [
112+ "scope:gmail.readonly"
113+ ]
114+ }
20115 }
21116 }
22117 }
.oxlintrc.jsonc+13−0
@@ -43,6 +43,8 @@ · 42 unmodified lines
4343 "executor-monorepo/no-cross-workspace-relative-imports": "error",
4444 "executor-monorepo/no-direct-effect-tag-read": "error",
4545 "executor-monorepo/no-effect-run-in-effect-vitest-tests": "error",
46+ "executor-monorepo/no-manual-httpapi-openapi-bridge": "error",
47+ "executor-monorepo/no-manual-openapi-documents": "error",
4648 "executor-monorepo/no-node-fs-with-effect-imports": "error",
4749 "executor-monorepo/no-raw-effect-fail-errors": "error",
4850 "executor-monorepo/no-yield-effect-fail": "error",
@@ -50,6 +52,17 @@ · 1 unmodified line
5052 },
5153
5254 "overrides": [
55+ {
56+ // SDK tests still use relative workspace imports to avoid introducing
57+ // package-graph cycles from test-only dependencies.
58+ "files": [
59+ "packages/platform/sdk/src/executor.test.ts",
60+ "packages/platform/sdk/src/runtime/executor-runtime.test.ts"
61+ ],
62+ "rules": {
63+ "executor-monorepo/no-cross-workspace-relative-imports": "off"
64+ }
65+ },
5366 {
5467 // JSON Schema conditionals legitimately use a `then` key.
5568 "files": [
.tmp-openapi-helper-probe.ts+30−0
@@ -0,0 +1,30 @@
1+import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, HttpApiSchema } from '@effect/platform';
2+import { startOpenApiTestServer } from '@executor/effect-test-utils';
3+import * as Effect from 'effect/Effect';
4+import * as Layer from 'effect/Layer';
5+import * as Schema from 'effect/Schema';
6+
7+const ownerParam = HttpApiSchema.param('owner', Schema.String);
8+const repoParam = HttpApiSchema.param('repo', Schema.String);
9+class DemoReposApi extends HttpApiGroup.make('repos').add(
10+ HttpApiEndpoint.get('getRepo')`/repos/${ownerParam}/${repoParam}`.addSuccess(
11+ Schema.Struct({ full_name: Schema.String, private: Schema.Boolean }),
12+ ),
13+) {}
14+class DemoApi extends HttpApi.make('demo').add(DemoReposApi) {}
15+const live = HttpApiBuilder.group(DemoApi, 'repos', (handlers) =>
16+ handlers.handle('getRepo', ({ path, request }) => {
17+ console.log('auth', request.headers.authorization);
18+ return Effect.succeed({ full_name: `${path.owner}/${path.repo}`, private: false });
19+ }),
20+);
21+const main = async () => {
22+ const server = await startOpenApiTestServer({ apiLayer: HttpApiBuilder.api(DemoApi).pipe(Layer.provide(live)) });
23+ console.log('base', server.baseUrl, server.specUrl);
24+ const spec = await fetch(server.specUrl);
25+ console.log('spec', spec.status, await spec.text());
26+ const resp = await fetch(`${server.baseUrl}/repos/vercel/ai`, { headers: { authorization: 'Bearer test' } });
27+ console.log('repo', resp.status, await resp.text());
28+ await server.close();
29+};
30+main().catch((e) => { console.error(e); process.exit(1); });
CLAUDE.md+7−0
@@ -0,0 +1,7 @@
1+# Repo Agent Notes
2+
3+- When a test needs an OpenAPI spec, generate it from Effect `HttpApi` with `OpenApi.fromApi(...)`.
4+- When a test needs to serve an Effect `HttpApi` plus `/openapi.json`, use `@executor/effect-test-utils` via `startOpenApiTestServer(...)` or `makeOpenApiTestServer(...)`.
5+- Do not hand-author inline OpenAPI documents in normal tests.
6+- Do not hand-roll `HttpApiBuilder.toWebHandler(...)` to Node HTTP bridges for OpenAPI test servers.
7+- Raw OpenAPI parser/import-fidelity tests are the exception; those may keep literal OpenAPI documents where the point of the test is the raw document itself.
apps/executor/package.json+1−0
@@ -45,6 +45,7 @@ · 44 unmodified lines
4545 "effect": "catalog:"
4646 },
4747 "devDependencies": {
48+ "@executor/effect-test-utils": "workspace:*",
4849 "@executor/mcp-elicitation-demo": "workspace:*",
4950 "@executor/runtime-ses": "workspace:*",
5051 "@effect/vitest": "catalog:",
apps/executor/src/cli/dev.ts+3−3
@@ -1,5 +1,5 @@
11 import type { ExecutorApiClient } from "@executor/platform-api";
2−import { WorkspaceIdSchema } from "@executor/platform-sdk/schema";
2+import { ScopeIdSchema } from "@executor/platform-sdk/schema";
33 import * as Effect from "effect/Effect";
44
55 const readBindingString = (binding: Record<string, unknown>, key: string): string | null =>
@@ -41,7 +41,7 @@ export const seedDemoMcpSourceInWorkspace = ( · 35 unmodified lines
4141 input: SeedDemoMcpSourceInput,
4242 ): Effect.Effect<SeedDemoMcpSourceResult, unknown, never> =>
4343 Effect.gen(function* () {
44− const workspaceId = WorkspaceIdSchema.make(input.workspaceId);
44+ const workspaceId = ScopeIdSchema.make(input.workspaceId);
4545
4646 const existing = yield* input.client.sources.list({
4747 path: {
@@ -138,7 +138,7 @@ export const seedGithubOpenApiSourceInWorkspace = ( · 90 unmodified lines
138138 input: SeedGithubOpenApiSourceInput,
139139 ): Effect.Effect<SeedDemoMcpSourceResult, unknown, never> =>
140140 Effect.gen(function* () {
141− const workspaceId = WorkspaceIdSchema.make(input.workspaceId);
141+ const workspaceId = ScopeIdSchema.make(input.workspaceId);
142142
143143 const existing = yield* input.client.sources.list({
144144 path: {
apps/executor/src/cli/main.ts+15−11
@@ -289,8 +289,8 @@ const loadRunWorkflowText = (): Effect.Effect<string, Error, never> => · 288 unmodified lines
289289 const environment = yield* Effect.gen(function* () {
290290 const resolveExecutionEnvironment = yield* RuntimeExecutionResolverService;
291291 return yield* resolveExecutionEnvironment({
292− workspaceId: executor.workspaceId,
293− accountId: executor.accountId,
292+ scopeId: executor.scopeId,
293+ actorScopeId: executor.actorScopeId,
294294 executionId: ExecutionIdSchema.make("exec_help"),
295295 });
296296 }).pipe(
@@ -403,7 +403,7 @@ const getLocalAuthedClient = (baseUrl: string = DEFAULT_SERVER_BASE_URL) => · 106 unmodified lines
403403 const installation = yield* bootstrapClient.local.installation({});
404404 const client = yield* createExecutorApiClient({
405405 baseUrl,
406− accountId: installation.accountId,
406+ accountId: installation.actorScopeId,
407407 });
408408
409409 return {
@@ -574,6 +574,10 @@ const getServerStatus = ( · 164 unmodified lines
574574 const installation = reachable
575575 ? yield* getBootstrapClient(baseUrl).pipe(
576576 Effect.flatMap((client) => client.local.installation({})),
577+ Effect.map((installation) => ({
578+ accountId: installation.actorScopeId,
579+ workspaceId: installation.scopeId,
580+ })),
577581 Effect.catchAll(() => Effect.succeed(null)),
578582 )
579583 : null;
@@ -923,7 +927,7 @@ const promptInteraction = (input: { · 343 unmodified lines
923927
924928 const waitForExecutionProgress = (input: {
925929 client: ExecutorApiClient;
926− workspaceId: ExecutionEnvelope["execution"]["workspaceId"];
930+ workspaceId: ExecutionEnvelope["execution"]["scopeId"];
927931 executionId: ExecutionEnvelope["execution"]["id"];
928932 pendingInteractionId: ExecutionInteraction["id"];
929933 }) =>
@@ -987,7 +991,7 @@ const seedDemoMcpSource = (input: { · 57 unmodified lines
987991 const { installation, client } = yield* getLocalAuthedClient(input.baseUrl);
988992 const result = yield* seedDemoMcpSourceInWorkspace({
989993 client,
990− workspaceId: installation.workspaceId,
994+ workspaceId: installation.scopeId,
991995 endpoint: input.endpoint,
992996 name: input.name,
993997 namespace: input.namespace,
@@ -1011,7 +1015,7 @@ const seedGithubOpenApiSource = (input: { · 17 unmodified lines
10111015 const { installation, client } = yield* getLocalAuthedClient(input.baseUrl);
10121016 const result = yield* seedGithubOpenApiSourceInWorkspace({
10131017 client,
1014− workspaceId: installation.workspaceId,
1018+ workspaceId: installation.scopeId,
10151019 endpoint: input.endpoint,
10161020 specUrl: input.specUrl,
10171021 name: input.name,
@@ -1026,7 +1030,7 @@ const seedGithubOpenApiSource = (input: { · 8 unmodified lines
10261030
10271031 const driveExecution = (input: {
10281032 client: ExecutorApiClient;
1029− workspaceId: ExecutionEnvelope["execution"]["workspaceId"];
1033+ workspaceId: ExecutionEnvelope["execution"]["scopeId"];
10301034 envelope: ExecutionEnvelope;
10311035 baseUrl: string;
10321036 shouldOpenUrls: boolean;
@@ -1297,7 +1301,7 @@ const callCommand = Command.make( · 264 unmodified lines
12971301 const { installation, client } = yield* getLocalAuthedClient(baseUrl);
12981302 const created = yield* client.executions.create({
12991303 path: {
1300− workspaceId: installation.workspaceId,
1304+ workspaceId: installation.scopeId,
13011305 },
13021306 payload: {
13031307 code: resolvedCode,
@@ -1307,7 +1311,7 @@ const callCommand = Command.make( · 3 unmodified lines
13071311
13081312 const settled = yield* driveExecution({
13091313 client,
1310− workspaceId: installation.workspaceId,
1314+ workspaceId: installation.scopeId,
13111315 envelope: created,
13121316 baseUrl,
13131317 shouldOpenUrls: !noOpen,
@@ -1333,14 +1337,14 @@ const resumeCommand = Command.make( · 19 unmodified lines
13331337 );
13341338 const execution = yield* client.executions.get({
13351339 path: {
1336− workspaceId: installation.workspaceId,
1340+ workspaceId: installation.scopeId,
13371341 executionId: decodedExecutionId,
13381342 },
13391343 });
13401344
13411345 const settled = yield* driveExecution({
13421346 client,
1343− workspaceId: installation.workspaceId,
1347+ workspaceId: installation.scopeId,
13441348 envelope: execution,
13451349 baseUrl,
13461350 shouldOpenUrls: !noOpen,
apps/executor/src/distribution/distribution.test.ts+6−6
@@ -65,8 +65,8 @@ describe("distribution flow", () => { · 64 unmodified lines
6565 const installationResponse = yield* harness.fetchText("/v1/local/installation");
6666 expect(installationResponse.status).toBe(200);
6767 const installation = JSON.parse(installationResponse.body) as {
68− workspaceId: string;
69− accountId: string;
68+ scopeId: string;
69+ actorScopeId: string;
7070 };
7171
7272 const sesCall = yield* runCommand(
@@ -88,12 +88,12 @@ describe("distribution flow", () => { · 15 unmodified lines
8888 const installationAfterRestart = JSON.parse(
8989 installationAfterRestartResponse.body,
9090 ) as {
91− workspaceId: string;
92− accountId: string;
91+ scopeId: string;
92+ actorScopeId: string;
9393 };
9494
95− expect(installationAfterRestart.workspaceId).toBe(installation.workspaceId);
96− expect(installationAfterRestart.accountId).toBe(installation.accountId);
95+ expect(installationAfterRestart.scopeId).toBe(installation.scopeId);
96+ expect(installationAfterRestart.actorScopeId).toBe(installation.actorScopeId);
9797
9898 yield* runCommand(["down", "--base-url", harness.baseUrl]);
9999 });
apps/executor/src/server/server.real-ingestion.test.ts+11−11
@@ -39,7 +39,7 @@ const createApiClientHarness = () => · 38 unmodified lines
3939 const installation = yield* bootstrapClient.local.installation({});
4040 const client = yield* createExecutorApiClient({
4141 baseUrl: server.baseUrl,
42− accountId: installation.accountId,
42+ accountId: installation.actorScopeId,
4343 });
4444
4545 return {
@@ -75,7 +75,7 @@ describe("local-executor-server real ingestion", () => { · 29 unmodified lines
7575
7676 const connected = yield* client.sources.connect({
7777 path: {
78− workspaceId: installation.workspaceId,
78+ workspaceId: installation.scopeId,
7979 },
8080 payload: {
8181 kind: "openapi",
@@ -96,7 +96,7 @@ describe("local-executor-server real ingestion", () => { · 14 unmodified lines
9696
9797 const inspection = yield* client.sources.inspection({
9898 path: {
99− workspaceId: installation.workspaceId,
99+ workspaceId: installation.scopeId,
100100 sourceId: connected.source.id,
101101 },
102102 });
@@ -106,7 +106,7 @@ describe("local-executor-server real ingestion", () => { · 3 unmodified lines
106106
107107 const discoveredTools = yield* client.sources.inspectionDiscover({
108108 path: {
109− workspaceId: installation.workspaceId,
109+ workspaceId: installation.scopeId,
110110 sourceId: connected.source.id,
111111 },
112112 payload: {
@@ -137,7 +137,7 @@ describe("local-executor-server real ingestion", () => { · 24 unmodified lines
137137
138138 const connected = yield* client.sources.connect({
139139 path: {
140− workspaceId: installation.workspaceId,
140+ workspaceId: installation.scopeId,
141141 },
142142 payload: {
143143 kind: "graphql",
@@ -159,7 +159,7 @@ describe("local-executor-server real ingestion", () => { · 15 unmodified lines
159159
160160 const inspection = yield* client.sources.inspection({
161161 path: {
162− workspaceId: installation.workspaceId,
162+ workspaceId: installation.scopeId,
163163 sourceId: connected.source.id,
164164 },
165165 });
@@ -175,7 +175,7 @@ describe("local-executor-server real ingestion", () => { · 9 unmodified lines
175175
176176 const toolDetail = yield* client.sources.inspectionTool({
177177 path: {
178− workspaceId: installation.workspaceId,
178+ workspaceId: installation.scopeId,
179179 sourceId: connected.source.id,
180180 toolPath: "linear.administrableTeams",
181181 },
@@ -191,7 +191,7 @@ describe("local-executor-server real ingestion", () => { · 9 unmodified lines
191191
192192 const storedSource = yield* client.sources.get({
193193 path: {
194− workspaceId: installation.workspaceId,
194+ workspaceId: installation.scopeId,
195195 sourceId: connected.source.id,
196196 },
197197 });
@@ -218,7 +218,7 @@ describe("local-executor-server real ingestion", () => { · 20 unmodified lines
218218
219219 const connected = yield* client.sources.connect({
220220 path: {
221− workspaceId: installation.workspaceId,
221+ workspaceId: installation.scopeId,
222222 },
223223 payload: {
224224 kind: "mcp",
@@ -235,7 +235,7 @@ describe("local-executor-server real ingestion", () => { · 10 unmodified lines
235235
236236 const inspection = yield* client.sources.inspection({
237237 path: {
238− workspaceId: installation.workspaceId,
238+ workspaceId: installation.scopeId,
239239 sourceId: connected.source.id,
240240 },
241241 });
@@ -245,7 +245,7 @@ describe("local-executor-server real ingestion", () => { · 3 unmodified lines
245245
246246 const discoveredTools = yield* client.sources.inspectionDiscover({
247247 path: {
248− workspaceId: installation.workspaceId,
248+ workspaceId: installation.scopeId,
249249 sourceId: connected.source.id,
250250 },
251251 payload: {
apps/executor/src/server/server.test.ts+111−181
@@ -1,4 +1,4 @@
1−import { createServer, type IncomingMessage, type ServerResponse } from "node:http";
1+import { type IncomingMessage, type ServerResponse } from "node:http";
22 import { randomUUID } from "node:crypto";
33
44 import {
@@ -6,17 +6,19 @@ import { · 1 unmodified line
66 HttpApiEndpoint,
77 HttpApiGroup,
88 HttpApiSchema,
9− OpenApi,
9+ HttpApiBuilder,
1010 FileSystem,
1111 } from "@effect/platform";
1212 import { NodeFileSystem } from "@effect/platform-node";
1313 import { describe, expect, it } from "@effect/vitest";
1414 import { assertTrue } from "@effect/vitest/utils";
1515 import * as Effect from "effect/Effect";
16+import * as Layer from "effect/Layer";
1617 import * as Schema from "effect/Schema";
1718
1819 import { startMcpElicitationDemoServer } from "@executor/mcp-elicitation-demo";
1920 import { makeToolInvokerFromTools, toTool } from "@executor/codemode-core";
21+import { startOpenApiTestServer } from "@executor/effect-test-utils";
2022 import {
2123 createExecutorApiClient,
2224 executorOpenApiSpec,
@@ -122,7 +124,7 @@ const writeConfiguredLocalMcpSource = (input: { · 99 unmodified lines
122124
123125 const source = {
124126 id: sourceId,
125− workspaceId: installation.workspaceId,
127+ scopeId: installation.scopeId,
126128 name: input.name ?? "Demo",
127129 kind: "mcp" as const,
128130 endpoint: input.endpoint,
@@ -196,7 +198,7 @@ const createApiClientHarness = () => · 67 unmodified lines
196198 const installation = yield* bootstrapClient.local.installation({});
197199 const client = yield* createExecutorApiClient({
198200 baseUrl: server.baseUrl,
199− accountId: installation.accountId,
201+ accountId: installation.actorScopeId,
200202 });
201203
202204 return {
@@ -288,161 +290,89 @@ const repoParam = HttpApiSchema.param("repo", Schema.String); · 85 unmodified lines
288290 class ExecutorDemoReposApi extends HttpApiGroup.make("repos")
289291 .add(
290292 HttpApiEndpoint.get("getRepo")`/repos/${ownerParam}/${repoParam}`
291− .addSuccess(
292− Schema.Struct({
293− full_name: Schema.String,
294− private: Schema.Boolean,
295− }),
296− ),
293+ .addSuccess(Schema.Unknown),
297294 )
298295 {}
299296
300297 class ExecutorDemoApi extends HttpApi.make("executorDemo").add(ExecutorDemoReposApi) {}
301298
302−const executorDemoOpenApiSpec = OpenApi.fromApi(ExecutorDemoApi);
299+class ExecutorDnsRecordsApi extends HttpApiGroup.make("records")
300+ .add(
301+ HttpApiEndpoint.post("createRecord")`/records`
302+ .setPayload(
303+ Schema.Struct({
304+ type: Schema.optional(Schema.String),
305+ name: Schema.optional(Schema.String),
306+ value: Schema.String,
307+ }),
308+ )
309+ .addSuccess(Schema.Unknown),
310+ )
311+{}
312+
313+class ExecutorDnsApi extends HttpApi.make("executorDns").add(
314+ ExecutorDnsRecordsApi,
315+) {}
303316
304317 const startOpenApiDemoServer = async () => {
305318 const seenAuthHeaders: Array<string | null> = [];
319+ const executorDemoLive = HttpApiBuilder.group(
320+ ExecutorDemoApi,
321+ "repos",
322+ (handlers) =>
323+ handlers.handle("getRepo", ({ path, request }) => {
324+ const authorization = request.headers.authorization;
325+ if (typeof authorization === "string") {
326+ seenAuthHeaders.push(authorization);
327+ } else {
328+ seenAuthHeaders.push(null);
329+ }
306330
307− const handler = (req: IncomingMessage, res: ServerResponse) => {
308− if (req.url === "/openapi.json") {
309− res.statusCode = 200;
310− res.setHeader("content-type", "application/json");
311− res.end(JSON.stringify(executorDemoOpenApiSpec));
312− return;
313− }
314−
315− const match = req.url?.match(/^\/repos\/([^/]+)\/([^/]+)$/);
316− if (req.method === "GET" && match) {
317− seenAuthHeaders.push(
318− typeof req.headers.authorization === "string" ? req.headers.authorization : null,
319− );
320− res.statusCode = 200;
321− res.setHeader("content-type", "application/json");
322− res.end(
323− JSON.stringify({
324− full_name: `${decodeURIComponent(match[1] ?? "")}/${decodeURIComponent(match[2] ?? "")}`,
331+ return Effect.succeed({
332+ full_name: `${path.owner}/${path.repo}`,
325333 private: false,
326− }),
327− );
328− return;
329− }
330−
331− res.statusCode = 404;
332− res.end("not found");
333− };
334+ });
335+ }),
336+ );
334337
335− const server = createServer(handler);
336− await new Promise<void>((resolve, reject) => {
337− server.once("error", reject);
338− server.listen(0, "127.0.0.1", () => resolve());
338+ const server = await startOpenApiTestServer({
339+ apiLayer: HttpApiBuilder.api(ExecutorDemoApi).pipe(
340+ Layer.provide(executorDemoLive),
341+ ),
339342 });
340− const address = server.address();
341− if (!address || typeof address === "string") {
342− throw new Error("Failed to bind OpenAPI demo server");
343− }
344343
345344 return {
346− baseUrl: `http://127.0.0.1:${address.port}`,
347− specUrl: `http://127.0.0.1:${address.port}/openapi.json`,
345+ ...server,
348346 seenAuthHeaders,
349− close: () =>
350− new Promise<void>((resolve, reject) => {
351− server.close((error) => (error ? reject(error) : resolve()));
352− }),
353347 };
354348 };
355349
356350 const startMutatingOpenApiDemoServer = async () => {
357351 const createdBodies: Array<Record<string, unknown>> = [];
358− const openApiDocument = JSON.stringify({
359− openapi: "3.0.3",
360− info: {
361− title: "Executor DNS Demo API",
362− version: "1.0.0",
363− },
364− paths: {
365− "/records": {
366− post: {
367− operationId: "records.createRecord",
368− tags: ["records"],
369− summary: "Create a DNS record",
370− requestBody: {
371− required: true,
372− content: {
373− "application/json": {
374− schema: {
375− type: "object",
376− properties: {
377− type: { type: "string" },
378− name: { type: "string" },
379− value: { type: "string" },
380− },
381− required: ["type", "value"],
382− },
383− },
384− },
385− },
386− responses: {
387− 200: {
388− description: "ok",
389− },
390− },
391− },
392− },
393− },
394− });
395−
396− const handler = (req: IncomingMessage, res: ServerResponse) => {
397− if (req.url === "/openapi.json") {
398− res.statusCode = 200;
399− res.setHeader("content-type", "application/json");
400− res.end(openApiDocument);
401− return;
402− }
403−
404− if (req.method === "POST" && req.url === "/records") {
405− const chunks: Array<Buffer> = [];
406− req.on("data", (chunk) => {
407− chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
408− });
409− req.on("end", () => {
410− const raw = Buffer.concat(chunks).toString("utf8");
411− const parsed = JSON.parse(raw) as Record<string, unknown>;
412− createdBodies.push(parsed);
413− res.statusCode = 200;
414− res.setHeader("content-type", "application/json");
415− res.end(JSON.stringify({
352+ const executorDnsLive = HttpApiBuilder.group(
353+ ExecutorDnsApi,
354+ "records",
355+ (handlers) =>
356+ handlers.handle("createRecord", ({ payload }) => {
357+ createdBodies.push(payload);
358+
359+ return Effect.succeed({
416360 ok: true,
417361 id: `rec_${createdBodies.length}`,
418− record: parsed,
419− }));
420− });
421− return;
422− }
423−
424− res.statusCode = 404;
425− res.end("not found");
426− };
362+ record: payload,
363+ });
364+ }),
365+ );
427366
428− const server = createServer(handler);
429− await new Promise<void>((resolve, reject) => {
430− server.once("error", reject);
431− server.listen(0, "127.0.0.1", () => resolve());
367+ const server = await startOpenApiTestServer({
368+ apiLayer: HttpApiBuilder.api(ExecutorDnsApi).pipe(
369+ Layer.provide(executorDnsLive),
370+ ),
432371 });
433− const address = server.address();
434− if (!address || typeof address === "string") {
435− throw new Error("Failed to bind mutating OpenAPI demo server");
436− }
437372
438373 return {
439− baseUrl: `http://127.0.0.1:${address.port}`,
440− specUrl: `http://127.0.0.1:${address.port}/openapi.json`,
374+ ...server,
441375 createdBodies,
442− close: () =>
443− new Promise<void>((resolve, reject) => {
444− server.close((error) => (error ? reject(error) : resolve()));
445− }),
446376 };
447377 };
448378
@@ -750,12 +680,12 @@ describe("local-executor-server", () => { · 301 unmodified lines
750680 const installation = yield* bootstrapClient.local.installation({});
751681 const client = yield* createExecutorApiClient({
752682 baseUrl: server.baseUrl,
753− accountId: installation.accountId,
683+ accountId: installation.actorScopeId,
754684 });
755685
756686 const execution = yield* client.executions.create({
757687 path: {
758− workspaceId: installation.workspaceId,
688+ workspaceId: installation.scopeId,
759689 },
760690 payload: {
761691 code: "return await tools.math.add({ a: 20, b: 22 });",
@@ -951,12 +881,12 @@ describe("local-executor-server", () => { · 189 unmodified lines
951881 const installation = yield* bootstrapClient.local.installation({});
952882 const client = yield* createExecutorApiClient({
953883 baseUrl: server.baseUrl,
954− accountId: installation.accountId,
884+ accountId: installation.actorScopeId,
955885 });
956886
957887 yield* client.sources.create({
958888 path: {
959− workspaceId: installation.workspaceId,
889+ workspaceId: installation.scopeId,
960890 },
961891 payload: {
962892 name: "Demo",
@@ -978,7 +908,7 @@ describe("local-executor-server", () => { · 15 unmodified lines
978908
979909 const created = yield* client.executions.create({
980910 path: {
981− workspaceId: installation.workspaceId,
911+ workspaceId: installation.scopeId,
982912 },
983913 payload: {
984914 code: 'return await tools.demo.gated_echo({ value: "from-daemon" });',
@@ -995,7 +925,7 @@ describe("local-executor-server", () => { · 10 unmodified lines
995925
996926 const approved = yield* client.executions.resume({
997927 path: {
998− workspaceId: installation.workspaceId,
928+ workspaceId: installation.scopeId,
999929 executionId: created.execution.id,
1000930 },
1001931 payload: {
@@ -1018,7 +948,7 @@ describe("local-executor-server", () => { · 16 unmodified lines
1018948
1019949 const resumed = yield* client.executions.resume({
1020950 path: {
1021− workspaceId: installation.workspaceId,
951+ workspaceId: installation.scopeId,
1022952 executionId: created.execution.id,
1023953 },
1024954 payload: {
@@ -1057,12 +987,12 @@ describe("local-executor-server", () => { · 32 unmodified lines
1057987 const installation = yield* bootstrapClient.local.installation({});
1058988 const client = yield* createExecutorApiClient({
1059989 baseUrl: server.baseUrl,
1060− accountId: installation.accountId,
990+ accountId: installation.actorScopeId,
1061991 });
1062992
1063993 yield* client.sources.create({
1064994 path: {
1065− workspaceId: installation.workspaceId,
995+ workspaceId: installation.scopeId,
1066996 },
1067997 payload: {
1068998 name: "Demo",
@@ -1085,7 +1015,7 @@ describe("local-executor-server", () => { · 16 unmodified lines
10851015 for (const value of ["first", "second"]) {
10861016 const created = yield* client.executions.create({
10871017 path: {
1088− workspaceId: installation.workspaceId,
1018+ workspaceId: installation.scopeId,
10891019 },
10901020 payload: {
10911021 code: `return await tools.demo.gated_echo({ value: "${value}" });`,
@@ -1098,7 +1028,7 @@ describe("local-executor-server", () => { · 6 unmodified lines
10981028
10991029 const approved = yield* client.executions.resume({
11001030 path: {
1101− workspaceId: installation.workspaceId,
1031+ workspaceId: installation.scopeId,
11021032 executionId: created.execution.id,
11031033 },
11041034 payload: {
@@ -1121,7 +1051,7 @@ describe("local-executor-server", () => { · 16 unmodified lines
11211051
11221052 const resumed = yield* client.executions.resume({
11231053 path: {
1124− workspaceId: installation.workspaceId,
1054+ workspaceId: installation.scopeId,
11251055 executionId: created.execution.id,
11261056 },
11271057 payload: {
@@ -1160,12 +1090,12 @@ describe("local-executor-server", () => { · 32 unmodified lines
11601090 const installation = yield* bootstrapClient.local.installation({});
11611091 const client = yield* createExecutorApiClient({
11621092 baseUrl: server.baseUrl,
1163− accountId: installation.accountId,
1093+ accountId: installation.actorScopeId,
11641094 });
11651095
11661096 const existing = yield* client.sources.create({
11671097 path: {
1168− workspaceId: installation.workspaceId,
1098+ workspaceId: installation.scopeId,
11691099 },
11701100 payload: {
11711101 name: "Demo",
@@ -1187,7 +1117,7 @@ describe("local-executor-server", () => { · 15 unmodified lines
11871117
11881118 const seeded = yield* seedDemoMcpSourceInWorkspace({
11891119 client,
1190− workspaceId: installation.workspaceId,
1120+ workspaceId: installation.scopeId,
11911121 endpoint: demoServer.endpoint,
11921122 name: "Demo",
11931123 namespace: "demo",
@@ -1198,7 +1128,7 @@ describe("local-executor-server", () => { · 4 unmodified lines
11981128
11991129 const sources = yield* client.sources.list({
12001130 path: {
1201− workspaceId: installation.workspaceId,
1131+ workspaceId: installation.scopeId,
12021132 },
12031133 });
12041134
@@ -1249,12 +1179,12 @@ describe("local-executor-server", () => { · 44 unmodified lines
12491179 const installation = yield* bootstrapClient.local.installation({});
12501180 const client = yield* createExecutorApiClient({
12511181 baseUrl: server.baseUrl,
1252− accountId: installation.accountId,
1182+ accountId: installation.actorScopeId,
12531183 });
12541184
12551185 yield* seedGithubOpenApiSourceInWorkspace({
12561186 client,
1257− workspaceId: installation.workspaceId,
1187+ workspaceId: installation.scopeId,
12581188 endpoint: openApiServer.baseUrl,
12591189 specUrl: openApiServer.specUrl,
12601190 name: "GitHub",
@@ -1263,7 +1193,7 @@ describe("local-executor-server", () => { · 2 unmodified lines
12631193
12641194 const execution = yield* client.executions.create({
12651195 path: {
1266− workspaceId: installation.workspaceId,
1196+ workspaceId: installation.scopeId,
12671197 },
12681198 payload: {
12691199 code: 'return await tools.github.repos.getRepo({ owner: "vercel", repo: "ai" });',
@@ -1287,7 +1217,7 @@ describe("local-executor-server", () => { · 17 unmodified lines
12871217
12881218 const connected = yield* client.sources.connect({
12891219 path: {
1290− workspaceId: installation.workspaceId,
1220+ workspaceId: installation.scopeId,
12911221 },
12921222 payload: {
12931223 kind: "openapi",
@@ -1309,7 +1239,7 @@ describe("local-executor-server", () => { · 15 unmodified lines
13091239
13101240 const sources = yield* client.sources.list({
13111241 path: {
1312− workspaceId: installation.workspaceId,
1242+ workspaceId: installation.scopeId,
13131243 },
13141244 });
13151245
@@ -1318,7 +1248,7 @@ describe("local-executor-server", () => { · 2 unmodified lines
13181248
13191249 const execution = yield* client.executions.create({
13201250 path: {
1321− workspaceId: installation.workspaceId,
1251+ workspaceId: installation.scopeId,
13221252 },
13231253 payload: {
13241254 code: 'return await tools.github.repos.getRepo({ owner: "vercel", repo: "ai" });',
@@ -1343,7 +1273,7 @@ describe("local-executor-server", () => { · 18 unmodified lines
13431273
13441274 const connected = yield* client.sources.connect({
13451275 path: {
1346− workspaceId: installation.workspaceId,
1276+ workspaceId: installation.scopeId,
13471277 },
13481278 payload: {
13491279 kind: "mcp",
@@ -1361,7 +1291,7 @@ describe("local-executor-server", () => { · 11 unmodified lines
13611291
13621292 const execution = yield* client.executions.create({
13631293 path: {
1364− workspaceId: installation.workspaceId,
1294+ workspaceId: installation.scopeId,
13651295 },
13661296 payload: {
13671297 code: 'return await tools.demo.gated_echo({ value: "from-api-client" });',
@@ -1379,7 +1309,7 @@ describe("local-executor-server", () => { · 11 unmodified lines
13791309
13801310 const approved = yield* client.executions.resume({
13811311 path: {
1382− workspaceId: installation.workspaceId,
1312+ workspaceId: installation.scopeId,
13831313 executionId: execution.execution.id,
13841314 },
13851315 payload: {
@@ -1405,7 +1335,7 @@ describe("local-executor-server", () => { · 19 unmodified lines
14051335
14061336 const resumed = yield* client.executions.resume({
14071337 path: {
1408− workspaceId: installation.workspaceId,
1338+ workspaceId: installation.scopeId,
14091339 executionId: execution.execution.id,
14101340 },
14111341 payload: {
@@ -1436,7 +1366,7 @@ describe("local-executor-server", () => { · 24 unmodified lines
14361366
14371367 const connected = yield* client.sources.connect({
14381368 path: {
1439− workspaceId: installation.workspaceId,
1369+ workspaceId: installation.scopeId,
14401370 },
14411371 payload: {
14421372 kind: "mcp",
@@ -1463,7 +1393,7 @@ describe("local-executor-server", () => { · 20 unmodified lines
14631393
14641394 const refreshedSource = yield* client.sources.get({
14651395 path: {
1466− workspaceId: installation.workspaceId,
1396+ workspaceId: installation.scopeId,
14671397 sourceId: connected.source.id,
14681398 },
14691399 });
@@ -1474,7 +1404,7 @@ describe("local-executor-server", () => { · 4 unmodified lines
14741404
14751405 const toolCall = yield* client.executions.create({
14761406 path: {
1477− workspaceId: installation.workspaceId,
1407+ workspaceId: installation.scopeId,
14781408 },
14791409 payload: {
14801410 code: "return await tools.axiom.whoami({});",
@@ -1491,7 +1421,7 @@ describe("local-executor-server", () => { · 10 unmodified lines
14911421
14921422 const approvedToolCall = yield* client.executions.resume({
14931423 path: {
1494− workspaceId: installation.workspaceId,
1424+ workspaceId: installation.scopeId,
14951425 executionId: toolCall.execution.id,
14961426 },
14971427 payload: {
@@ -1529,12 +1459,12 @@ describe("local-executor-server", () => { · 31 unmodified lines
15291459 const installation = yield* bootstrapClient.local.installation({});
15301460 const client = yield* createExecutorApiClient({
15311461 baseUrl: server.baseUrl,
1532− accountId: installation.accountId,
1462+ accountId: installation.actorScopeId,
15331463 });
15341464
15351465 const added = yield* client.executions.create({
15361466 path: {
1537− workspaceId: installation.workspaceId,
1467+ workspaceId: installation.scopeId,
15381468 },
15391469 payload: {
15401470 code: `return await tools.executor.sources.add({ endpoint: ${JSON.stringify(oauthServer.endpoint)}, name: "Axiom", namespace: "axiom" });`,
@@ -1563,7 +1493,7 @@ describe("local-executor-server", () => { · 22 unmodified lines
15631493 while (true) {
15641494 const sources = yield* client.sources.list({
15651495 path: {
1566− workspaceId: installation.workspaceId,
1496+ workspaceId: installation.scopeId,
15671497 },
15681498 });
15691499 const source = sources.find((entry) => entry.namespace === "axiom");
@@ -1581,7 +1511,7 @@ describe("local-executor-server", () => { · 11 unmodified lines
15811511
15821512 const toolCall = yield* client.executions.create({
15831513 path: {
1584− workspaceId: installation.workspaceId,
1514+ workspaceId: installation.scopeId,
15851515 },
15861516 payload: {
15871517 code: "return await tools.axiom.whoami({});",
@@ -1597,7 +1527,7 @@ describe("local-executor-server", () => { · 9 unmodified lines
15971527
15981528 const approvedToolCall = yield* client.executions.resume({
15991529 path: {
1600− workspaceId: installation.workspaceId,
1530+ workspaceId: installation.scopeId,
16011531 executionId: toolCall.execution.id,
16021532 },
16031533 payload: {
@@ -1633,12 +1563,12 @@ describe("local-executor-server", () => { · 29 unmodified lines
16331563 const installation = yield* bootstrapClient.local.installation({});
16341564 const client = yield* createExecutorApiClient({
16351565 baseUrl: server.baseUrl,
1636− accountId: installation.accountId,
1566+ accountId: installation.actorScopeId,
16371567 });
16381568
16391569 const connected = yield* client.sources.connect({
16401570 path: {
1641− workspaceId: installation.workspaceId,
1571+ workspaceId: installation.scopeId,
16421572 },
16431573 payload: {
16441574 kind: "openapi",
@@ -1655,7 +1585,7 @@ describe("local-executor-server", () => { · 10 unmodified lines
16551585
16561586 const gated = yield* client.executions.create({
16571587 path: {
1658− workspaceId: installation.workspaceId,
1588+ workspaceId: installation.scopeId,
16591589 },
16601590 payload: {
16611591 code: 'return await tools.dns.records.createRecord({ body: { type: "TXT", name: "", value: "hello world" } });',
@@ -1668,12 +1598,12 @@ describe("local-executor-server", () => { · 6 unmodified lines
16681598 throw new Error("Expected pending approval interaction");
16691599 }
16701600 expect(gated.pendingInteraction.kind).toBe("form");
1671− expect(gated.pendingInteraction.payloadJson).toContain("Allow Create a DNS record?");
1601+ expect(gated.pendingInteraction.payloadJson).toContain("Allow records.createRecord?");
16721602 expect(gated.pendingInteraction.payloadJson).toContain("\"approve\"");
16731603
16741604 const approved = yield* client.executions.resume({
16751605 path: {
1676− workspaceId: installation.workspaceId,
1606+ workspaceId: installation.scopeId,
16771607 executionId: gated.execution.id,
16781608 },
16791609 payload: {
@@ -1691,7 +1621,7 @@ describe("local-executor-server", () => { · 11 unmodified lines
16911621
16921622 const policy = yield* client.policies.create({
16931623 path: {
1694− workspaceId: installation.workspaceId,
1624+ workspaceId: installation.scopeId,
16951625 },
16961626 payload: {
16971627 resourcePattern: "dns.records.createRecord",
@@ -1703,7 +1633,7 @@ describe("local-executor-server", () => { · 5 unmodified lines
17031633
17041634 const automatic = yield* client.executions.create({
17051635 path: {
1706− workspaceId: installation.workspaceId,
1636+ workspaceId: installation.scopeId,
17071637 },
17081638 payload: {
17091639 code: 'return await tools.dns.records.createRecord({ body: { type: "TXT", name: "", value: "hello again" } });',
@@ -1735,11 +1665,11 @@ describe("local-executor-server", () => { · 25 unmodified lines
17351665 const installation = yield* bootstrapClient.local.installation({});
17361666
17371667 const startResponse = yield* Effect.promise(() =>
1738− fetch(`${server.baseUrl}/v1/workspaces/${encodeURIComponent(installation.workspaceId)}/oauth/source-auth/start`, {
1668+ fetch(`${server.baseUrl}/v1/workspaces/${encodeURIComponent(installation.scopeId)}/oauth/source-auth/start`, {
17391669 method: "POST",
17401670 headers: {
17411671 "content-type": "application/json",
1742− "x-executor-account-id": installation.accountId,
1672+ "x-executor-account-id": installation.actorScopeId,
17431673 },
17441674 body: JSON.stringify({
17451675 provider: "mcp",
@@ -1774,11 +1704,11 @@ describe("local-executor-server", () => { · 28 unmodified lines
17741704
17751705 const client = yield* createExecutorApiClient({
17761706 baseUrl: server.baseUrl,
1777− accountId: installation.accountId,
1707+ accountId: installation.actorScopeId,
17781708 });
17791709 const sources = yield* client.sources.list({
17801710 path: {
1781− workspaceId: installation.workspaceId,
1711+ workspaceId: installation.scopeId,
17821712 },
17831713 });
17841714
@@ -1804,7 +1734,7 @@ describe("local-executor-server", () => { · 19 unmodified lines
18041734 const installation = yield* bootstrapClient.local.installation({});
18051735 const client = yield* createExecutorApiClient({
18061736 baseUrl: server.baseUrl,
1807− accountId: installation.accountId,
1737+ accountId: installation.actorScopeId,
18081738 });
18091739 yield* writeConfiguredLocalMcpSource({
18101740 workspaceRoot: server.workspaceRoot,
@@ -1816,7 +1746,7 @@ describe("local-executor-server", () => { · 5 unmodified lines
18161746
18171747 const execution = yield* client.executions.create({
18181748 path: {
1819− workspaceId: installation.workspaceId,
1749+ workspaceId: installation.scopeId,
18201750 },
18211751 payload: {
18221752 code: 'return await tools.demo.gated_echo({ value: "broken" });',
@@ -1832,7 +1762,7 @@ describe("local-executor-server", () => { · 9 unmodified lines
18321762
18331763 const resumed = yield* client.executions.resume({
18341764 path: {
1835− workspaceId: installation.workspaceId,
1765+ workspaceId: installation.scopeId,
18361766 executionId: execution.execution.id,
18371767 },
18381768 payload: {
apps/web/src/views/add-source.tsx+2−2
@@ -484,7 +484,7 @@ const buildConnectPayload = (form: ConnectFormState): ConnectSourcePayload => { · 483 unmodified lines
484484 discoveryUrl: trimToNull(form.discoveryUrl),
485485 name: trimToNull(form.name),
486486 namespace: trimToNull(form.namespace),
487− workspaceOauthClientId:
487+ scopeOauthClientId:
488488 form.authKind === "oauth2" &&
489489 form.workspaceOauthClientId.trim().length > 0
490490 ? form.workspaceOauthClientId.trim()
@@ -1114,7 +1114,7 @@ export function AddSourcePage() { · 623 unmodified lines
11141114 selectedGoogleTemplateIds.includes(template.id),
11151115 );
11161116 const payload: ConnectSourceBatchPayload = {
1117− workspaceOauthClientId:
1117+ scopeOauthClientId:
11181118 workspaceOauthClientId as WorkspaceOauthClient["id"],
11191119 sources: selectedTemplates.map((template) => ({
11201120 service: template.service,
bun.lock+19−0
@@ -36,6 +36,7 @@ · 35 unmodified lines
3636 },
3737 "devDependencies": {
3838 "@effect/vitest": "catalog:",
39+ "@executor/effect-test-utils": "workspace:*",
3940 "@executor/mcp-elicitation-demo": "workspace:*",
4041 "@executor/runtime-ses": "workspace:*",
4142 "@modelcontextprotocol/sdk": "^1.26.0",
@@ -150,6 +151,7 @@ · 108 unmodified lines
150151 "@effect/platform": "catalog:",
151152 "@effect/platform-node": "catalog:",
152153 "@effect/vitest": "catalog:",
154+ "@executor/effect-test-utils": "workspace:*",
153155 "@executor/platform-sdk-file": "workspace:*",
154156 "@types/jsdom": "^21.1.7",
155157 "@types/node": "catalog:",
@@ -164,6 +166,20 @@ · 8 unmodified lines
164166 "react": ">=18 <20",
165167 },
166168 },
169+ "packages/dev/effect-test-utils": {
170+ "name": "@executor/effect-test-utils",
171+ "version": "0.0.0",
172+ "dependencies": {
173+ "@effect/platform": "catalog:",
174+ "@effect/platform-node": "catalog:",
175+ "effect": "catalog:",
176+ },
177+ "devDependencies": {
178+ "@types/node": "catalog:",
179+ "bun-types": "catalog:",
180+ "typescript": "^5.9.3",
181+ },
182+ },
167183 "packages/dev/kitchen-sink": {
168184 "name": "@executor/kitchen-sink",
169185 "version": "0.0.0",
@@ -337,6 +353,7 @@ · 167 unmodified lines
337353 },
338354 "devDependencies": {
339355 "@effect/vitest": "catalog:",
356+ "@executor/effect-test-utils": "workspace:*",
340357 "@modelcontextprotocol/sdk": "^1.26.0",
341358 "@types/node": "catalog:",
342359 "bun-types": "catalog:",
@@ -709,6 +726,8 @@ · 366 unmodified lines
709726
710727 "@executor/docs": ["@executor/docs@workspace:apps/docs"],
711728
729+ "@executor/effect-test-utils": ["@executor/effect-test-utils@workspace:packages/dev/effect-test-utils"],
730+
712731 "@executor/executor-mcp": ["@executor/executor-mcp@workspace:packages/hosts/mcp"],
713732
714733 "@executor/ir": ["@executor/ir@workspace:packages/kernel/ir"],
examples/sqlite-sdk-consumer/src/main.ts+2−2
@@ -5,8 +5,8 @@ const databasePath = process.env.DATABASE_PATH · 4 unmodified lines
55
66 const executor = await createSqliteExecutor({
77 databasePath,
8− workspaceName: "SQLite Example Workspace",
9− workspaceRoot: process.cwd(),
8+ scopeName: "SQLite Example Workspace",
9+ scopeRoot: process.cwd(),
1010 });
1111
1212 try {
examples/sqlite-sdk-consumer/src/sqlite-backend.ts+84−81
@@ -16,13 +16,13 @@ import { · 15 unmodified lines
1616 type ExecutorSecretMaterialBackend,
1717 type ExecutorSourceArtifactBackend,
1818 type ExecutorSourceTypeDeclarationsBackend,
19− type ExecutorWorkspaceConfigBackend,
20− type ExecutorWorkspaceStateBackend,
19+ type ExecutorScopeConfigBackend,
20+ type ExecutorScopeStateBackend,
2121 } from "@executor/platform-sdk";
2222 import type { ExecutorRuntimeOptions } from "@executor/platform-sdk/runtime";
2323 import {
2424 type AuthArtifact,
25− AccountIdSchema,
25+ ScopeIdSchema,
2626 AuthArtifactSchema,
2727 type AuthLease,
2828 AuthLeaseSchema,
@@ -43,11 +43,10 @@ import { · 14 unmodified lines
4343 SourceAuthSessionSchema,
4444 SourceCatalogIdSchema,
4545 SourceCatalogRevisionIdSchema,
46− WorkspaceIdSchema,
47− type WorkspaceOauthClient,
48− WorkspaceOauthClientSchema,
49− type WorkspaceSourceOauthClient,
50− WorkspaceSourceOauthClientSchema,
46+ type ScopeOauthClient,
47+ ScopeOauthClientSchema,
48+ type ScopedSourceOauthClient,
49+ ScopedSourceOauthClientSchema,
5150 type SecretMaterial,
5251 type SecretRef,
5352 } from "@executor/platform-sdk/schema";
@@ -59,24 +58,24 @@ import { contentHash, snapshotFromSourceCatalogSyncResult } from "@executor/sour · 5 unmodified lines
5958
6059 export type CreateSqliteExecutorBackendOptions = {
6160 databasePath?: string;
62− workspaceName?: string;
63− workspaceRoot?: string | null;
64− workspaceId?: string;
65− accountId?: string;
61+ scopeName?: string;
62+ scopeRoot?: string | null;
63+ scopeId?: string;
64+ actorScopeId?: string;
6665 };
6766
6867 type DocumentNamespace =
6968 | "installation"
70− | "workspace_config"
71− | "workspace_state"
69+ | "scope_config"
70+ | "scope_state"
7271 | "source_artifact"
7372 | "executor_state";
7473
7574 type ExecutorStateCollections = {
7675 authArtifacts: readonly AuthArtifact[];
7776 authLeases: readonly AuthLease[];
78− sourceOauthClients: readonly WorkspaceSourceOauthClient[];
79− workspaceOauthClients: readonly WorkspaceOauthClient[];
77+ sourceOauthClients: readonly ScopedSourceOauthClient[];
78+ scopeOauthClients: readonly ScopeOauthClient[];
8079 providerAuthGrants: readonly ProviderAuthGrant[];
8180 sourceAuthSessions: readonly SourceAuthSession[];
8281 secretMaterials: readonly SecretMaterial[];
@@ -90,10 +89,10 @@ const decodeConfig = Schema.decodeUnknownSync(LocalExecutorConfigSchema); · 7 unmodified lines
9089 const decodeAuthArtifacts = Schema.decodeUnknownSync(Schema.Array(AuthArtifactSchema));
9190 const decodeAuthLeases = Schema.decodeUnknownSync(Schema.Array(AuthLeaseSchema));
9291 const decodeSourceOauthClients = Schema.decodeUnknownSync(
93− Schema.Array(WorkspaceSourceOauthClientSchema),
92+ Schema.Array(ScopedSourceOauthClientSchema),
9493 );
95−const decodeWorkspaceOauthClients = Schema.decodeUnknownSync(
96− Schema.Array(WorkspaceOauthClientSchema),
94+const decodeScopeOauthClients = Schema.decodeUnknownSync(
95+ Schema.Array(ScopeOauthClientSchema),
9796 );
9897 const decodeProviderAuthGrants = Schema.decodeUnknownSync(
9998 Schema.Array(ProviderAuthGrantSchema),
@@ -117,10 +116,10 @@ type SourceArtifactBuildInput = Parameters<ExecutorSourceArtifactBackend["build" · 17 unmodified lines
117116
118117 const SQLITE_SECRET_PROVIDER_ID = "sqlite";
119118
120−type WorkspaceState = Parameters<ExecutorWorkspaceStateBackend["write"]>[0];
119+type ScopeState = Parameters<ExecutorScopeStateBackend["write"]>[0];
121120 const defaultProjectConfig = decodeConfig({});
122121
123−const defaultWorkspaceState = (): WorkspaceState => ({
122+const defaultScopeState = (): ScopeState => ({
124123 version: 1 as const,
125124 sources: {},
126125 policies: {},
@@ -258,8 +257,12 @@ const createInstallationStore = ( · 131 unmodified lines
258257 ): ExecutorInstallationBackend => {
259258 const provision = (): LocalInstallation =>
260259 decodeInstallation({
261− accountId: AccountIdSchema.make(options.accountId ?? "acct_sqlite_example"),
262− workspaceId: WorkspaceIdSchema.make(options.workspaceId ?? "ws_sqlite_example"),
260+ actorScopeId: ScopeIdSchema.make(options.actorScopeId ?? "acct_sqlite_example"),
261+ scopeId: ScopeIdSchema.make(options.scopeId ?? "ws_sqlite_example"),
262+ resolutionScopeIds: [
263+ ScopeIdSchema.make(options.scopeId ?? "ws_sqlite_example"),
264+ ScopeIdSchema.make(options.actorScopeId ?? "acct_sqlite_example"),
265+ ],
263266 });
264267
265268 return {
@@ -276,13 +279,13 @@ const createInstallationStore = ( · 10 unmodified lines
276279 };
277280 };
278281
279−const createWorkspaceConfigStore = (
282+const createScopeConfigStore = (
280283 store: SqliteDocumentStore,
281−): ExecutorWorkspaceConfigBackend => ({
284+): ExecutorScopeConfigBackend => ({
282285 load: () =>
283286 effectTry("load workspace config", () => {
284287 const projectConfig = store.read(
285− "workspace_config",
288+ "scope_config",
286289 "project",
287290 decodeConfig,
288291 () => defaultProjectConfig,
@@ -296,22 +299,22 @@ const createWorkspaceConfigStore = ( · 7 unmodified lines
296299 }),
297300 writeProject: (config) =>
298301 effectTry("write workspace config", () => {
299− store.write("workspace_config", "project", config);
302+ store.write("scope_config", "project", config);
300303 }),
301− resolveRelativePath: ({ path, workspaceRoot }) =>
302− resolvePath(workspaceRoot, path),
304+ resolveRelativePath: ({ path, scopeRoot }) =>
305+ resolvePath(scopeRoot, path),
303306 });
304307
305−const createWorkspaceStateStore = (
308+const createScopeStateStore = (
306309 store: SqliteDocumentStore,
307−): ExecutorWorkspaceStateBackend => ({
310+): ExecutorScopeStateBackend => ({
308311 load: () =>
309312 effectTry("load workspace state", () =>
310− store.read("workspace_state", "active", (value) => value as ReturnType<typeof defaultWorkspaceState>, defaultWorkspaceState),
313+ store.read("scope_state", "active", (value) => value as ReturnType<typeof defaultScopeState>, defaultScopeState),
311314 ),
312315 write: (state) =>
313316 effectTry("write workspace state", () => {
314− store.write("workspace_state", "active", state);
317+ store.write("scope_state", "active", state);
315318 }),
316319 });
317320
@@ -351,8 +354,8 @@ const createExecutorStateStore = ( · 33 unmodified lines
351354 return store.read("executor_state", key, decodeAuthLeases, () => []) as ExecutorStateCollections[K];
352355 case "sourceOauthClients":
353356 return store.read("executor_state", key, decodeSourceOauthClients, () => []) as ExecutorStateCollections[K];
354− case "workspaceOauthClients":
355− return store.read("executor_state", key, decodeWorkspaceOauthClients, () => []) as ExecutorStateCollections[K];
357+ case "scopeOauthClients":
358+ return store.read("executor_state", key, decodeScopeOauthClients, () => []) as ExecutorStateCollections[K];
356359 case "providerAuthGrants":
357360 return store.read("executor_state", key, decodeProviderAuthGrants, () => []) as ExecutorStateCollections[K];
358361 case "sourceAuthSessions":
@@ -387,23 +390,23 @@ const createExecutorStateStore = ( · 28 unmodified lines
387390
388391 return {
389392 authArtifacts: {
390− listByWorkspaceId: (workspaceId) =>
393+ listByScopeId: (scopeId) =>
391394 effectTry("list auth artifacts by workspace", () =>
392− readCollection("authArtifacts").filter((item) => item.workspaceId === workspaceId),
395+ readCollection("authArtifacts").filter((item) => item.scopeId === scopeId),
393396 ),
394− listByWorkspaceAndSourceId: ({ workspaceId, sourceId }) =>
397+ listByScopeAndSourceId: ({ scopeId, sourceId }) =>
395398 effectTry("list auth artifacts by source", () =>
396399 readCollection("authArtifacts").filter((item) =>
397− item.workspaceId === workspaceId && item.sourceId === sourceId
400+ item.scopeId === scopeId && item.sourceId === sourceId
398401 ),
399402 ),
400− getByWorkspaceSourceAndActor: ({ workspaceId, sourceId, actorAccountId, slot }) =>
403+ getByScopeSourceAndActor: ({ scopeId, sourceId, actorScopeId, slot }) =>
401404 effectTry("get auth artifact", () =>
402405 Option.fromNullable(
403406 readCollection("authArtifacts").find((item) =>
404− item.workspaceId === workspaceId
407+ item.scopeId === scopeId
405408 && item.sourceId === sourceId
406− && sameActor(item.actorAccountId, actorAccountId)
409+ && sameActor(item.actorScopeId, actorScopeId)
407410 && item.slot === slot
408411 ),
409412 ),
@@ -412,9 +415,9 @@ const createExecutorStateStore = ( · 2 unmodified lines
412415 effectTry("upsert auth artifact", () => {
413416 updateCollection("authArtifacts", (items) => {
414417 const index = items.findIndex((item) =>
415− item.workspaceId === artifact.workspaceId
418+ item.scopeId === artifact.scopeId
416419 && item.sourceId === artifact.sourceId
417− && sameActor(item.actorAccountId, artifact.actorAccountId)
420+ && sameActor(item.actorScopeId, artifact.actorScopeId)
418421 && item.slot === artifact.slot
419422 );
420423 if (index >= 0) {
@@ -424,25 +427,25 @@ const createExecutorStateStore = ( · 3 unmodified lines
424427 }
425428 });
426429 }),
427− removeByWorkspaceSourceAndActor: ({ workspaceId, sourceId, actorAccountId, slot }) =>
430+ removeByScopeSourceAndActor: ({ scopeId, sourceId, actorScopeId, slot }) =>
428431 effectTry("remove auth artifact", () =>
429432 updateCollection("authArtifacts", (items) => {
430433 const before = items.length;
431434 const remaining = items.filter((item) => !(
432− item.workspaceId === workspaceId
435+ item.scopeId === scopeId
433436 && item.sourceId === sourceId
434− && sameActor(item.actorAccountId, actorAccountId)
437+ && sameActor(item.actorScopeId, actorScopeId)
435438 && (slot === undefined || item.slot === slot)
436439 ));
437440 items.splice(0, items.length, ...remaining);
438441 return before !== remaining.length;
439442 }),
440443 ),
441− removeByWorkspaceAndSourceId: ({ workspaceId, sourceId }) =>
444+ removeByScopeAndSourceId: ({ scopeId, sourceId }) =>
442445 effectTry("remove auth artifacts by source", () =>
443446 updateCollection("authArtifacts", (items) => {
444447 const remaining = items.filter((item) =>
445− !(item.workspaceId === workspaceId && item.sourceId === sourceId)
448+ !(item.scopeId === scopeId && item.sourceId === sourceId)
446449 );
447450 const removed = items.length - remaining.length;
448451 items.splice(0, items.length, ...remaining);
@@ -481,11 +484,11 @@ const createExecutorStateStore = ( · 32 unmodified lines
481484 ),
482485 },
483486 sourceOauthClients: {
484− getByWorkspaceSourceAndProvider: ({ workspaceId, sourceId, providerKey }) =>
487+ getByScopeSourceAndProvider: ({ scopeId, sourceId, providerKey }) =>
485488 effectTry("get source oauth client", () =>
486489 Option.fromNullable(
487490 readCollection("sourceOauthClients").find((item) =>
488− item.workspaceId === workspaceId
491+ item.scopeId === scopeId
489492 && item.sourceId === sourceId
490493 && item.providerKey === providerKey
491494 ),
@@ -495,7 +498,7 @@ const createExecutorStateStore = ( · 3 unmodified lines
495498 effectTry("upsert source oauth client", () => {
496499 updateCollection("sourceOauthClients", (items) => {
497500 const index = items.findIndex((item) =>
498− item.workspaceId === oauthClient.workspaceId
501+ item.scopeId === oauthClient.scopeId
499502 && item.sourceId === oauthClient.sourceId
500503 && item.providerKey === oauthClient.providerKey
501504 );
@@ -506,11 +509,11 @@ const createExecutorStateStore = ( · 4 unmodified lines
506509 }
507510 });
508511 }),
509− removeByWorkspaceAndSourceId: ({ workspaceId, sourceId }) =>
512+ removeByScopeAndSourceId: ({ scopeId, sourceId }) =>
510513 effectTry("remove source oauth clients", () =>
511514 updateCollection("sourceOauthClients", (items) => {
512515 const remaining = items.filter((item) =>
513− !(item.workspaceId === workspaceId && item.sourceId === sourceId)
516+ !(item.scopeId === scopeId && item.sourceId === sourceId)
514517 );
515518 const removed = items.length - remaining.length;
516519 items.splice(0, items.length, ...remaining);
@@ -518,22 +521,22 @@ const createExecutorStateStore = ( · 1 unmodified line
518521 }),
519522 ),
520523 },
521− workspaceOauthClients: {
522− listByWorkspaceAndProvider: ({ workspaceId, providerKey }) =>
524+ scopeOauthClients: {
525+ listByScopeAndProvider: ({ scopeId, providerKey }) =>
523526 effectTry("list workspace oauth clients", () =>
524− readCollection("workspaceOauthClients").filter((item) =>
525− item.workspaceId === workspaceId && item.providerKey === providerKey
527+ readCollection("scopeOauthClients").filter((item) =>
528+ item.scopeId === scopeId && item.providerKey === providerKey
526529 ),
527530 ),
528531 getById: (id) =>
529532 effectTry("get workspace oauth client", () =>
530533 Option.fromNullable(
531− readCollection("workspaceOauthClients").find((item) => item.id === id),
534+ readCollection("scopeOauthClients").find((item) => item.id === id),
532535 ),
533536 ),
534537 upsert: (oauthClient) =>
535538 effectTry("upsert workspace oauth client", () => {
536− updateCollection("workspaceOauthClients", (items) => {
539+ updateCollection("scopeOauthClients", (items) => {
537540 const index = items.findIndex((item) => item.id === oauthClient.id);
538541 if (index >= 0) {
539542 items[index] = oauthClient;
@@ -544,7 +547,7 @@ const createExecutorStateStore = ( · 4 unmodified lines
544547 }),
545548 removeById: (id) =>
546549 effectTry("remove workspace oauth client", () =>
547− updateCollection("workspaceOauthClients", (items) => {
550+ updateCollection("scopeOauthClients", (items) => {
548551 const remaining = items.filter((item) => item.id !== id);
549552 const removed = remaining.length !== items.length;
550553 items.splice(0, items.length, ...remaining);
@@ -553,15 +556,15 @@ const createExecutorStateStore = ( · 2 unmodified lines
553556 ),
554557 },
555558 providerAuthGrants: {
556− listByWorkspaceId: (workspaceId) =>
559+ listByScopeId: (scopeId) =>
557560 effectTry("list provider grants by workspace", () =>
558− readCollection("providerAuthGrants").filter((item) => item.workspaceId === workspaceId),
561+ readCollection("providerAuthGrants").filter((item) => item.scopeId === scopeId),
559562 ),
560− listByWorkspaceActorAndProvider: ({ workspaceId, actorAccountId, providerKey }) =>
563+ listByScopeActorAndProvider: ({ scopeId, actorScopeId, providerKey }) =>
561564 effectTry("list provider grants by actor", () =>
562565 readCollection("providerAuthGrants").filter((item) =>
563− item.workspaceId === workspaceId
564− && item.actorAccountId === actorAccountId
566+ item.scopeId === scopeId
567+ && item.actorScopeId === actorScopeId
565568 && item.providerKey === providerKey
566569 ),
567570 ),
@@ -595,9 +598,9 @@ const createExecutorStateStore = ( · 27 unmodified lines
595598 sourceAuthSessions: {
596599 listAll: () =>
597600 effectTry("list source auth sessions", () => readCollection("sourceAuthSessions")),
598− listByWorkspaceId: (workspaceId) =>
601+ listByScopeId: (scopeId) =>
599602 effectTry("list source auth sessions by workspace", () =>
600− readCollection("sourceAuthSessions").filter((item) => item.workspaceId === workspaceId),
603+ readCollection("sourceAuthSessions").filter((item) => item.scopeId === scopeId),
601604 ),
602605 getById: (id) =>
603606 effectTry("get source auth session", () =>
@@ -611,13 +614,13 @@ const createExecutorStateStore = ( · 7 unmodified lines
611614 readCollection("sourceAuthSessions").find((item) => item.state === state),
612615 ),
613616 ),
614− getPendingByWorkspaceSourceAndActor: ({ workspaceId, sourceId, actorAccountId, credentialSlot }) =>
617+ getPendingByScopeSourceAndActor: ({ scopeId, sourceId, actorScopeId, credentialSlot }) =>
615618 effectTry("get pending source auth session", () =>
616619 Option.fromNullable(
617620 readCollection("sourceAuthSessions").find((item) =>
618− item.workspaceId === workspaceId
621+ item.scopeId === scopeId
619622 && item.sourceId === sourceId
620− && item.actorAccountId === actorAccountId
623+ && item.actorScopeId === actorScopeId
621624 && item.status === "pending"
622625 && (credentialSlot === undefined || item.credentialSlot === credentialSlot)
623626 ),
@@ -656,11 +659,11 @@ const createExecutorStateStore = ( · 32 unmodified lines
656659 }
657660 });
658661 }),
659− removeByWorkspaceAndSourceId: (workspaceId, sourceId) =>
662+ removeByScopeAndSourceId: (scopeId, sourceId) =>
660663 effectTry("remove source auth sessions", () =>
661664 updateCollection("sourceAuthSessions", (items) => {
662665 const remaining = items.filter((item) =>
663− !(item.workspaceId === workspaceId && item.sourceId === sourceId)
666+ !(item.scopeId === scopeId && item.sourceId === sourceId)
664667 );
665668 const removed = remaining.length !== items.length;
666669 items.splice(0, items.length, ...remaining);
@@ -739,11 +742,11 @@ const createExecutorStateStore = ( · 72 unmodified lines
739742 readCollection("executions").find((item) => item.id === executionId),
740743 ),
741744 ),
742− getByWorkspaceAndId: (workspaceId, executionId) =>
743− effectTry("get execution by workspace and id", () =>
745+ getByScopeAndId: (scopeId, executionId) =>
746+ effectTry("get execution by scope and id", () =>
744747 Option.fromNullable(
745748 readCollection("executions").find((item) =>
746− item.workspaceId === workspaceId && item.id === executionId
749+ item.scopeId === scopeId && item.id === executionId
747750 ),
748751 ),
749752 ),
@@ -932,9 +935,9 @@ export const createSqliteExecutorBackend = ( · 182 unmodified lines
932935 const secretMaterial = createSecretMaterialBackend(executorState);
933936
934937 return {
935− workspace: {
936− workspaceName: options.workspaceName ?? "SQLite SDK Example",
937− workspaceRoot: options.workspaceRoot ?? null,
938+ scope: {
939+ scopeName: options.scopeName ?? "SQLite SDK Example",
940+ scopeRoot: options.scopeRoot ?? null,
938941 metadata: {
939942 kind: "sqlite",
940943 databasePath,
@@ -942,8 +945,8 @@ export const createSqliteExecutorBackend = ( · 1 unmodified line
942945 },
943946 storage: {
944947 installation: createInstallationStore(store, options),
945− workspaceConfig: createWorkspaceConfigStore(store),
946− workspaceState: createWorkspaceStateStore(store),
948+ scopeConfig: createScopeConfigStore(store),
949+ scopeState: createScopeStateStore(store),
947950 sourceArtifacts: createSourceArtifactStore(store),
948951 executorState,
949952 secretMaterial,
packages/clients/react/package.json+1−0
@@ -24,6 +24,7 @@ · 23 unmodified lines
2424 "@effect/platform": "catalog:",
2525 "@effect/platform-node": "catalog:",
2626 "@effect/vitest": "catalog:",
27+ "@executor/effect-test-utils": "workspace:*",
2728 "@executor/platform-sdk-file": "workspace:*",
2829 "@types/jsdom": "^21.1.7",
2930 "@types/node": "catalog:",
packages/clients/react/src/index.test.tsx+46−107
@@ -1,8 +1,19 @@
11 import { createServer } from "node:http";
22
3−import { FileSystem, HttpApiBuilder, HttpServer } from "@effect/platform";
3+import {
4+ FileSystem,
5+ HttpApi,
6+ HttpApiBuilder,
7+ HttpApiEndpoint,
8+ HttpApiGroup,
9+ HttpServer,
10+ OpenApi,
11+} from "@effect/platform";
412 import { NodeFileSystem, NodeHttpServer } from "@effect/platform-node";
513 import { describe, expect, it } from "@effect/vitest";
14+import {
15+ startOpenApiTestServer,
16+} from "@executor/effect-test-utils";
617 import {
718 createExecutorApiLayer,
819 } from "@executor/platform-api";
@@ -17,6 +28,7 @@ import * as Context from "effect/Context"; · 8 unmodified lines
1728 import * as Effect from "effect/Effect";
1829 import * as Exit from "effect/Exit";
1930 import * as Layer from "effect/Layer";
31+import * as Schema from "effect/Schema";
2032 import * as Scope from "effect/Scope";
2133 import { JSDOM } from "jsdom";
2234 import * as React from "react";
@@ -225,83 +237,38 @@ type OpenApiSpecServer = RunningServer & { · 202 unmodified lines
225237 specUrl: string;
226238 };
227239
228−const startOpenApiSpecServer = async (): Promise<OpenApiSpecServer> => {
229− const spec = {
230− openapi: "3.0.3",
231− info: {
232− title: "Hook Test API",
233− version: "1.0.0",
234− },
235− paths: {
236− "/ping": {
237− get: {
238− operationId: "ping",
239− responses: {
240− 200: {
241− description: "ok",
242− content: {
243− "application/json": {
244− schema: {
245− type: "object",
246− properties: {
247− ok: { type: "boolean" },
248− },
249− },
250− },
251− },
252− },
253− },
254− },
255− },
256− },
257− };
258−
259− const server = createServer((req, res) => {
260− if (req.url === "/openapi.json") {
261− res.statusCode = 200;
262− res.setHeader("content-type", "application/json");
263− res.end(JSON.stringify(spec));
264− return;
265− }
266−
267− if (req.url === "/ping") {
268− res.statusCode = 200;
269− res.setHeader("content-type", "application/json");
270− res.end(JSON.stringify({ ok: true }));
271− return;
272− }
273−
274− res.statusCode = 404;
275− res.end("not found");
276− });
240+class HooksTestPingApi extends HttpApiGroup.make("ping")
241+ .add(
242+ HttpApiEndpoint.get("ping")`/ping`
243+ .addSuccess(
244+ Schema.Struct({
245+ ok: Schema.Boolean,
246+ }),
247+ ),
248+ )
249+{}
250+
251+class HooksTestOpenApi extends HttpApi.make("hooksTest").add(HooksTestPingApi) {}
252+
253+const hooksTestOpenApiSpec = OpenApi.fromApi(HooksTestOpenApi);
254+const hooksTestOpenApiLive = HttpApiBuilder.group(
255+ HooksTestOpenApi,
256+ "ping",
257+ (handlers) =>
258+ handlers.handle("ping", () =>
259+ Effect.succeed({
260+ ok: true,
261+ }),
262+ ),
263+);
264+const hooksTestOpenApiLayer = HttpApiBuilder.api(HooksTestOpenApi).pipe(
265+ Layer.provide(hooksTestOpenApiLive),
266+);
277267
278− await new Promise<void>((resolve, reject) => {
279− server.once("error", reject);
280− server.listen(0, "127.0.0.1", () => resolve());
268+const startOpenApiSpecServer = async (): Promise<OpenApiSpecServer> => {
269+ return startOpenApiTestServer({
270+ apiLayer: hooksTestOpenApiLayer,
281271 });
282−
283− const address = server.address();
284− if (!address || typeof address === "string") {
285− throw new Error("Failed to bind OpenAPI spec server");
286− }
287−
288− const baseUrl = `http://127.0.0.1:${address.port}`;
289−
290− return {
291− baseUrl,
292− specUrl: `${baseUrl}/openapi.json`,
293− close: () =>
294− new Promise<void>((resolve, reject) => {
295− server.close((error) => {
296− if (error) {
297− reject(error);
298− return;
299− }
300−
301− resolve();
302− });
303− }),
304− };
305272 };
306273
307274 async function requestJson<T>(input: {
@@ -343,42 +310,14 @@ const seedStoredOpenApiSource = async (input: { · 35 unmodified lines
343310 name: string;
344311 specUrl?: string;
345312 }): Promise<Source> => {
346− const sourceDocumentText = JSON.stringify({
347− openapi: "3.0.3",
348− info: {
349− title: input.name,
350− version: "1.0.0",
351− },
352− paths: {
353− "/ping": {
354− get: {
355− operationId: "ping",
356− responses: {
357− 200: {
358− description: "ok",
359− content: {
360− "application/json": {
361− schema: {
362− type: "object",
363− properties: {
364− ok: { type: "boolean" },
365− },
366− },
367− },
368− },
369− },
370− },
371− },
372− },
373− },
374− });
313+ const sourceDocumentText = JSON.stringify(hooksTestOpenApiSpec);
375314 const specUrl = input.specUrl ?? `data:application/json,${encodeURIComponent(sourceDocumentText)}`;
376315
377316 return requestJson<Source>({
378317 baseUrl: input.server.baseUrl,
379− path: `/v1/workspaces/${input.installation.workspaceId}/sources`,
318+ path: `/v1/workspaces/${input.installation.scopeId}/sources`,
380319 method: "POST",
381− accountId: input.installation.accountId,
320+ accountId: input.installation.actorScopeId,
382321 payload: {
383322 name: input.name,
384323 kind: "openapi",
packages/clients/react/src/index.ts+21−21
@@ -32,7 +32,7 @@ import type { · 31 unmodified lines
3232 SourceInspection,
3333 SourceInspectionDiscoverResult,
3434 SourceInspectionToolDetail,
35− WorkspaceOauthClient,
35+ ScopeOauthClient as WorkspaceOauthClient,
3636 } from "@executor/platform-sdk/schema";
3737 import * as Cause from "effect/Cause";
3838 import * as Effect from "effect/Effect";
@@ -42,7 +42,7 @@ import * as Runtime from "effect/Runtime"; · 3 unmodified lines
4242 import * as React from "react";
4343
4444 const DEFAULT_EXECUTOR_API_BASE_URL = "http://127.0.0.1:8788";
45−const PLACEHOLDER_WORKSPACE_ID = "ws_placeholder" as Source["workspaceId"];
45+const PLACEHOLDER_WORKSPACE_ID = "ws_placeholder" as Source["scopeId"];
4646 const PLACEHOLDER_ACCOUNT_ID = "acc_placeholder";
4747 const PLACEHOLDER_SOURCE_ID = "src_placeholder" as Source["id"];
4848
@@ -58,18 +58,18 @@ export type SourceRemoveResult = { · 9 unmodified lines
5858
5959 type AtomKeyPart = string | number | boolean | null | undefined;
6060
61−type SourcesKeyParts = readonly [boolean, Source["workspaceId"], string];
62−type SourceKeyParts = readonly [boolean, Source["workspaceId"], string, Source["id"]];
61+type SourcesKeyParts = readonly [boolean, Source["scopeId"], string];
62+type SourceKeyParts = readonly [boolean, Source["scopeId"], string, Source["id"]];
6363 type SourceToolDetailKeyParts = readonly [
6464 boolean,
65− Source["workspaceId"],
65+ Source["scopeId"],
6666 string,
6767 Source["id"],
6868 string | null,
6969 ];
7070 type SourceDiscoveryKeyParts = readonly [
7171 boolean,
72− Source["workspaceId"],
72+ Source["scopeId"],
7373 string,
7474 Source["id"],
7575 string,
@@ -77,13 +77,13 @@ type SourceDiscoveryKeyParts = readonly [ · 1 unmodified line
7777 ];
7878 type WorkspaceOauthClientsKeyParts = readonly [
7979 boolean,
80− Source["workspaceId"],
80+ Source["scopeId"],
8181 string,
8282 string,
8383 ];
8484
8585 type InvalidationTarget = {
86− workspaceId?: Source["workspaceId"];
86+ workspaceId?: Source["scopeId"];
8787 accountId?: string;
8888 sourceId?: Source["id"];
8989 };
@@ -104,7 +104,7 @@ type ExecutorQueryContextValue = { · 14 unmodified lines
104104 };
105105
106106 type MutationExecutionContext = {
107− workspaceId: Source["workspaceId"];
107+ workspaceId: Source["scopeId"];
108108 accountId: string;
109109 registry: Registry.Registry;
110110 invalidateQueries: (target?: InvalidationTarget) => void;
@@ -149,20 +149,20 @@ const decodeAtomKey = <T extends ReadonlyArray<AtomKeyPart>>(key: string): T => · 38 unmodified lines
149149
150150 const encodeSourcesKey = (
151151 enabled: boolean,
152− workspaceId: Source["workspaceId"],
152+ workspaceId: Source["scopeId"],
153153 accountId: string,
154154 ): string => encodeAtomKey([enabled, workspaceId, accountId] satisfies SourcesKeyParts);
155155
156156 const encodeSourceKey = (
157157 enabled: boolean,
158− workspaceId: Source["workspaceId"],
158+ workspaceId: Source["scopeId"],
159159 accountId: string,
160160 sourceId: Source["id"],
161161 ): string => encodeAtomKey([enabled, workspaceId, accountId, sourceId] satisfies SourceKeyParts);
162162
163163 const encodeToolDetailKey = (
164164 enabled: boolean,
165− workspaceId: Source["workspaceId"],
165+ workspaceId: Source["scopeId"],
166166 accountId: string,
167167 sourceId: Source["id"],
168168 toolPath: string | null,
@@ -171,7 +171,7 @@ const encodeToolDetailKey = ( · 2 unmodified lines
171171
172172 const encodeDiscoveryKey = (
173173 enabled: boolean,
174− workspaceId: Source["workspaceId"],
174+ workspaceId: Source["scopeId"],
175175 accountId: string,
176176 sourceId: Source["id"],
177177 query: string,
@@ -181,7 +181,7 @@ const encodeDiscoveryKey = ( · 3 unmodified lines
181181
182182 const encodeWorkspaceOauthClientsKey = (
183183 enabled: boolean,
184− workspaceId: Source["workspaceId"],
184+ workspaceId: Source["scopeId"],
185185 accountId: string,
186186 providerKey: string,
187187 ): string =>
@@ -430,7 +430,7 @@ export type Loadable<T> = · 242 unmodified lines
430430
431431 type WorkspaceContext = {
432432 installation: LocalInstallation;
433− workspaceId: Source["workspaceId"];
433+ workspaceId: Source["scopeId"];
434434 accountId: string;
435435 };
436436
@@ -483,8 +483,8 @@ const useWorkspaceContext = (): Loadable<WorkspaceContext> => { · 46 unmodified lines
483483 status: "ready",
484484 data: {
485485 installation: installation.data,
486− workspaceId: installation.data.workspaceId,
487− accountId: installation.data.accountId,
486+ workspaceId: installation.data.scopeId,
487+ accountId: installation.data.actorScopeId,
488488 },
489489 } satisfies Loadable<WorkspaceContext>;
490490 }, [installation]);
@@ -559,7 +559,7 @@ const createActiveQueryCollections = (): ActiveQueryCollections => ({ · 68 unmodified lines
559559
560560 const targetMatches = (
561561 target: InvalidationTarget | undefined,
562− workspaceId: Source["workspaceId"],
562+ workspaceId: Source["scopeId"],
563563 accountId: string,
564564 sourceId?: Source["id"],
565565 ): boolean => {
@@ -671,14 +671,14 @@ const removeSourceFromList = ( · 105 unmodified lines
671671 ): ReadonlyArray<Source> => sources.filter((source) => source.id !== sourceId);
672672
673673 const createOptimisticSource = (input: {
674− workspaceId: Source["workspaceId"];
674+ workspaceId: Source["scopeId"];
675675 payload: CreateSourcePayload;
676676 }): Source => {
677677 const now = Date.now();
678678
679679 return {
680680 id: `src_optimistic_${crypto.randomUUID()}` as Source["id"],
681− workspaceId: input.workspaceId,
681+ scopeId: input.workspaceId,
682682 name: input.payload.name,
683683 kind: input.payload.kind,
684684 endpoint: input.payload.endpoint,
@@ -713,7 +713,7 @@ const applyUpdatePayloadToSource = (source: Source, payload: UpdateSourcePayload · 28 unmodified lines
713713
714714 const useSourceMutation = <TInput, TOutput, TOptimistic = never>(
715715 execute: (input: {
716− workspaceId: Source["workspaceId"];
716+ workspaceId: Source["scopeId"];
717717 accountId: string;
718718 payload: TInput;
719719 }) => Promise<TOutput>,
packages/dev/effect-test-utils/package.json+22−0
@@ -0,0 +1,22 @@
1+{
2+ "name": "@executor/effect-test-utils",
3+ "private": true,
4+ "type": "module",
5+ "exports": {
6+ ".": "./src/index.ts"
7+ },
8+ "scripts": {
9+ "typecheck": "bunx tsc --noEmit -p tsconfig.json"
10+ },
11+ "dependencies": {
12+ "@effect/platform": "catalog:",
13+ "@effect/platform-node": "catalog:",
14+ "effect": "catalog:"
15+ },
16+ "devDependencies": {
17+ "@types/node": "catalog:",
18+ "bun-types": "catalog:",
19+ "typescript": "^5.9.3"
20+ },
21+ "version": "0.0.0"
22+}
packages/dev/effect-test-utils/src/index.ts+103−0
@@ -0,0 +1,103 @@
1+import { createServer } from "node:http";
2+
3+import { HttpApiBuilder, HttpServer } from "@effect/platform";
4+import { NodeHttpServer } from "@effect/platform-node";
5+import * as Context from "effect/Context";
6+import * as Effect from "effect/Effect";
7+import * as Exit from "effect/Exit";
8+import * as Layer from "effect/Layer";
9+import * as Scope from "effect/Scope";
10+
11+export type RunningHttpApiTestServer = {
12+ baseUrl: string;
13+ close: () => Promise<void>;
14+};
15+
16+export type RunningOpenApiTestServer = RunningHttpApiTestServer & {
17+ specUrl: string;
18+};
19+
20+type TestApiLayer = Layer.Layer<any, any, never>;
21+
22+type HttpApiTestServerOptions = {
23+ apiLayer: TestApiLayer;
24+ host?: string;
25+ openApiPath?: `/${string}`;
26+};
27+
28+const createHttpApiServerLayer = (options: HttpApiTestServerOptions) => {
29+ const host = options.host ?? "127.0.0.1";
30+ const openApiPath = options.openApiPath;
31+ const apiLayerWithOpenApi =
32+ openApiPath === undefined
33+ ? options.apiLayer
34+ : Layer.mergeAll(
35+ options.apiLayer,
36+ HttpApiBuilder.middlewareOpenApi({ path: openApiPath }).pipe(
37+ Layer.provideMerge(options.apiLayer),
38+ ),
39+ );
40+
41+ return HttpApiBuilder.serve().pipe(
42+ Layer.provide(apiLayerWithOpenApi),
43+ Layer.provideMerge(
44+ NodeHttpServer.layer(() => createServer(), {
45+ port: 0,
46+ host,
47+ }),
48+ ),
49+ );
50+};
51+
52+export const startHttpApiTestServer = async (
53+ options: HttpApiTestServerOptions,
54+): Promise<RunningHttpApiTestServer> => {
55+ const scope = await Effect.runPromise(Scope.make());
56+
57+ try {
58+ const context = await Effect.runPromise(
59+ Layer.buildWithScope(createHttpApiServerLayer(options), scope),
60+ );
61+ const server = Context.get(context, HttpServer.HttpServer);
62+ const baseUrl = HttpServer.formatAddress(server.address);
63+
64+ return {
65+ baseUrl,
66+ close: () => Effect.runPromise(Scope.close(scope, Exit.void)),
67+ };
68+ } catch (error) {
69+ await Effect.runPromise(Scope.close(scope, Exit.void));
70+ throw error;
71+ }
72+};
73+
74+export const startOpenApiTestServer = async (
75+ options: HttpApiTestServerOptions,
76+): Promise<RunningOpenApiTestServer> => {
77+ const openApiPath = options.openApiPath ?? "/openapi.json";
78+ const server = await startHttpApiTestServer({
79+ ...options,
80+ openApiPath,
81+ });
82+
83+ return {
84+ ...server,
85+ specUrl: `${server.baseUrl}${openApiPath}`,
86+ };
87+};
88+
89+export const makeHttpApiTestServer = (
90+ options: HttpApiTestServerOptions,
91+): Effect.Effect<RunningHttpApiTestServer, never, Scope.Scope> =>
92+ Effect.acquireRelease(
93+ Effect.promise(() => startHttpApiTestServer(options)),
94+ (server) => Effect.promise(() => server.close()).pipe(Effect.orDie),
95+ );
96+
97+export const makeOpenApiTestServer = (
98+ options: HttpApiTestServerOptions,
99+): Effect.Effect<RunningOpenApiTestServer, never, Scope.Scope> =>
100+ Effect.acquireRelease(
101+ Effect.promise(() => startOpenApiTestServer(options)),
102+ (server) => Effect.promise(() => server.close()).pipe(Effect.orDie),
103+ );
packages/dev/effect-test-utils/tsconfig.json+15−0
@@ -0,0 +1,15 @@
1+{
2+ "compilerOptions": {
3+ "target": "ES2022",
4+ "module": "ESNext",
5+ "moduleResolution": "Bundler",
6+ "strict": true,
7+ "skipLibCheck": true,
8+ "noEmit": true,
9+ "lib": ["ES2022"],
10+ "types": ["bun-types", "node"]
11+ },
12+ "include": [
13+ "src/**/*.ts"
14+ ]
15+}
packages/hosts/mcp/src/index.ts+24−24
@@ -154,8 +154,8 @@ const loadExecuteDescription = (runtime: ExecutorRuntime): Promise<string> => · 153 unmodified lines
154154 Effect.gen(function* () {
155155 const resolveExecutionEnvironment = yield* RuntimeExecutionResolverService;
156156 const environment = yield* resolveExecutionEnvironment({
157− workspaceId: runtime.localInstallation.workspaceId,
158− accountId: runtime.localInstallation.accountId,
157+ scopeId: runtime.localInstallation.scopeId,
158+ actorScopeId: runtime.localInstallation.actorScopeId,
159159 executionId: ExecutionIdSchema.make("exec_mcp_help"),
160160 });
161161
@@ -260,7 +260,7 @@ const buildToolResult = (envelope: ExecutionEnvelope): ExecutorMcpToolResult => · 98 unmodified lines
260260
261261 const waitForInteractionProgress = async (input: {
262262 runtime: ExecutorRuntime;
263− workspaceId: string;
263+ scopeId: string;
264264 executionId: string;
265265 pendingInteractionId: string;
266266 }): Promise<ExecutionEnvelope> => {
@@ -268,7 +268,7 @@ const waitForInteractionProgress = async (input: { · 1 unmodified line
268268 const next = await runControlPlane(
269269 input.runtime,
270270 getExecution({
271− workspaceId: input.workspaceId as never,
271+ scopeId: input.scopeId as never,
272272 executionId: input.executionId as never,
273273 }),
274274 );
@@ -286,8 +286,8 @@ const waitForInteractionProgress = async (input: { · 11 unmodified lines
286286
287287 const driveExecutionWithElicitation = async (input: {
288288 runtime: ExecutorRuntime;
289− workspaceId: string;
290− accountId: string;
289+ scopeId: string;
290+ actorScopeId: string;
291291 server: McpServer;
292292 envelope: ExecutionEnvelope;
293293 }): Promise<ExecutionEnvelope> => {
@@ -317,13 +317,13 @@ const driveExecutionWithElicitation = async (input: { · 23 unmodified lines
317317 current = await runControlPlane(
318318 input.runtime,
319319 resumeExecution({
320− workspaceId: input.workspaceId as never,
320+ scopeId: input.scopeId as never,
321321 executionId: current.execution.id as never,
322322 payload: {
323323 responseJson: JSON.stringify(response),
324324 interactionMode: interactionModeForServer(input.server),
325325 },
326− resumedByAccountId: input.accountId as never,
326+ resumedByScopeId: input.actorScopeId as never,
327327 }),
328328 );
329329 continue;
@@ -340,13 +340,13 @@ const driveExecutionWithElicitation = async (input: { · 10 unmodified lines
340340 current = await runControlPlane(
341341 input.runtime,
342342 resumeExecution({
343− workspaceId: input.workspaceId as never,
343+ scopeId: input.scopeId as never,
344344 executionId: current.execution.id as never,
345345 payload: {
346346 responseJson: JSON.stringify(response),
347347 interactionMode: interactionModeForServer(input.server),
348348 },
349− resumedByAccountId: input.accountId as never,
349+ resumedByScopeId: input.actorScopeId as never,
350350 }),
351351 );
352352 continue;
@@ -354,7 +354,7 @@ const driveExecutionWithElicitation = async (input: { · 1 unmodified line
354354
355355 current = await waitForInteractionProgress({
356356 runtime: input.runtime,
357− workspaceId: input.workspaceId,
357+ scopeId: input.scopeId,
358358 executionId: current.execution.id,
359359 pendingInteractionId: pending.id,
360360 });
@@ -365,15 +365,15 @@ const driveExecutionWithElicitation = async (input: { · 4 unmodified lines
365365
366366 const driveExecutionWithoutElicitation = async (input: {
367367 runtime: ExecutorRuntime;
368− workspaceId: string;
369− accountId: string;
368+ scopeId: string;
369+ actorScopeId: string;
370370 executionId: string;
371371 initialResponse?: ResumeResponseInput;
372372 }): Promise<ExecutionEnvelope> => {
373373 let current = await runControlPlane(
374374 input.runtime,
375375 getExecution({
376− workspaceId: input.workspaceId as never,
376+ scopeId: input.scopeId as never,
377377 executionId: input.executionId as never,
378378 }),
379379 );
@@ -388,13 +388,13 @@ const driveExecutionWithoutElicitation = async (input: { · 8 unmodified lines
388388 current = await runControlPlane(
389389 input.runtime,
390390 resumeExecution({
391− workspaceId: input.workspaceId as never,
391+ scopeId: input.scopeId as never,
392392 executionId: current.execution.id as never,
393393 payload: {
394394 responseJson: JSON.stringify(response),
395395 interactionMode: "detach",
396396 },
397− resumedByAccountId: input.accountId as never,
397+ resumedByScopeId: input.actorScopeId as never,
398398 }),
399399 );
400400 response = undefined;
@@ -416,8 +416,8 @@ const createExecutorMcpServer = async (config: { · 15 unmodified lines
416416 },
417417 );
418418
419− const workspaceId = config.runtime.localInstallation.workspaceId;
420− const accountId = config.runtime.localInstallation.accountId;
419+ const scopeId = config.runtime.localInstallation.scopeId;
420+ const actorScopeId = config.runtime.localInstallation.actorScopeId;
421421
422422 const executeTool = server.registerTool(
423423 "execute",
@@ -429,20 +429,20 @@ const createExecutorMcpServer = async (config: { · 5 unmodified lines
429429 let created = await runControlPlane(
430430 config.runtime,
431431 createExecution({
432− workspaceId,
432+ scopeId,
433433 payload: {
434434 code,
435435 interactionMode: interactionModeForServer(server),
436436 },
437− createdByAccountId: accountId,
437+ createdByScopeId: actorScopeId,
438438 }),
439439 );
440440
441441 if (supportsManagedElicitation(server)) {
442442 created = await driveExecutionWithElicitation({
443443 runtime: config.runtime,
444− workspaceId,
445− accountId,
444+ scopeId,
445+ actorScopeId,
446446 server,
447447 envelope: created,
448448 });
@@ -469,8 +469,8 @@ const createExecutorMcpServer = async (config: { · 20 unmodified lines
469469 ) => {
470470 const resumed = await driveExecutionWithoutElicitation({
471471 runtime: config.runtime,
472− workspaceId,
473− accountId,
472+ scopeId,
473+ actorScopeId,
474474 executionId: input.resumePayload.executionId,
475475 initialResponse: input.response,
476476 });
packages/hosts/mcp/src/paused-result.test.ts+2−2
@@ -35,8 +35,8 @@ const makeEnvelope = ( · 34 unmodified lines
3535 ): ExecutionEnvelope => ({
3636 execution: {
3737 id: "exec_123" as never,
38− workspaceId: "ws_123" as never,
39− createdByAccountId: "acct_123" as never,
38+ scopeId: "ws_123" as never,
39+ createdByScopeId: "acct_123" as never,
4040 status: "waiting_for_interaction",
4141 code: "return await tools.executor.sources.add({ endpoint: 'https://mcp.axiom.co/mcp' });",
4242 resultJson: null,
packages/platform/api/src/executions/api.ts+1−1
@@ -10,7 +10,7 @@ export type { · 9 unmodified lines
1010 import {
1111 ExecutionIdSchema,
1212 ExecutionEnvelopeSchema,
13− WorkspaceIdSchema,
13+ ScopeIdSchema as WorkspaceIdSchema,
1414 } from "@executor/platform-sdk/schema";
1515
1616 import {
packages/platform/api/src/executor-context.ts+3−3
@@ -1,5 +1,5 @@
11 import type { Executor } from "@executor/platform-sdk";
2−import type { WorkspaceId } from "@executor/platform-sdk/schema";
2+import type { ScopeId as WorkspaceId } from "@executor/platform-sdk/schema";
33 import {
44 ControlPlaneBadRequestError,
55 ControlPlaneForbiddenError,
@@ -23,13 +23,13 @@ export const resolveRequestedLocalWorkspace = ( · 17 unmodified lines
2323 workspaceId: WorkspaceId,
2424 ) =>
2525 Effect.flatMap(ControlPlaneExecutor, (executor) =>
26− executor.workspaceId === workspaceId
26+ executor.scopeId === workspaceId
2727 ? Effect.succeed(executor)
2828 : Effect.fail(
2929 new ControlPlaneForbiddenError({
3030 operation,
3131 message: "Requested workspace is not the active local workspace",
32− details: `requestedWorkspaceId=${workspaceId} activeWorkspaceId=${executor.workspaceId}`,
32+ details: `requestedWorkspaceId=${workspaceId} activeWorkspaceId=${executor.scopeId}`,
3333 }),
3434 ),
3535 );
packages/platform/api/src/oauth/api.ts+1−1
@@ -4,7 +4,7 @@ import { · 3 unmodified lines
44 SourceAuthSessionIdSchema,
55 SourceTransportSchema,
66 StringMapSchema,
7− WorkspaceIdSchema,
7+ ScopeIdSchema as WorkspaceIdSchema,
88 } from "@executor/platform-sdk/schema";
99 import * as Schema from "effect/Schema";
1010
packages/platform/api/src/oauth/http.ts+1−1
@@ -3,7 +3,7 @@ import { · 2 unmodified lines
33 HttpServerRequest,
44 HttpServerResponse,
55 } from "@effect/platform";
6−import type { WorkspaceId } from "@executor/platform-sdk/schema";
6+import type { ScopeId as WorkspaceId } from "@executor/platform-sdk/schema";
77 import * as Cause from "effect/Cause";
88 import * as Either from "effect/Either";
99 import * as Effect from "effect/Effect";
packages/platform/api/src/policies/api.ts+2−2
@@ -11,9 +11,9 @@ import { · 10 unmodified lines
1111 ControlPlaneUnauthorizedError,
1212 } from "@executor/platform-sdk/errors";
1313 import {
14− LocalWorkspacePolicySchema,
14+ LocalScopePolicySchema as LocalWorkspacePolicySchema,
1515 PolicyIdSchema,
16− WorkspaceIdSchema,
16+ ScopeIdSchema as WorkspaceIdSchema,
1717 } from "@executor/platform-sdk/schema";
1818 import * as Schema from "effect/Schema";
1919
packages/platform/api/src/sources/api.ts+6−6
@@ -5,13 +5,13 @@ import { · 4 unmodified lines
55 ConnectSourcePayloadSchema,
66 ConnectSourceResultSchema,
77 CreateSourcePayloadSchema,
8− CreateWorkspaceOauthClientPayloadSchema,
8+ CreateScopeOauthClientPayloadSchema as CreateWorkspaceOauthClientPayloadSchema,
99 CredentialOauthCompleteUrlParamsSchema,
1010 CredentialPageUrlParamsSchema,
1111 CredentialSubmitPayloadSchema,
1212 DiscoverSourcePayloadSchema,
1313 UpdateSourcePayloadSchema,
14− WorkspaceOauthClientQuerySchema,
14+ ScopeOauthClientQuerySchema as WorkspaceOauthClientQuerySchema,
1515 } from "@executor/platform-sdk/contracts";
1616 import {
1717 ControlPlaneBadRequestError,
@@ -29,9 +29,9 @@ import { · 11 unmodified lines
2929 SourceInspectionSchema,
3030 SourceInspectionToolDetailSchema,
3131 SourceSchema,
32− WorkspaceOauthClientIdSchema,
33− WorkspaceOauthClientSchema,
34− WorkspaceIdSchema,
32+ ScopeOauthClientIdSchema as WorkspaceOauthClientIdSchema,
33+ ScopeOauthClientSchema as WorkspaceOauthClientSchema,
34+ ScopeIdSchema as WorkspaceIdSchema,
3535 } from "@executor/platform-sdk/schema";
3636 import * as Schema from "effect/Schema";
3737
@@ -41,7 +41,7 @@ export type { · 3 unmodified lines
4141 ConnectSourcePayload,
4242 ConnectSourceResult,
4343 CreateSourcePayload,
44− CreateWorkspaceOauthClientPayload,
44+ CreateScopeOauthClientPayload as CreateWorkspaceOauthClientPayload,
4545 DiscoverSourcePayload,
4646 UpdateSourcePayload,
4747 } from "@executor/platform-sdk/contracts";
packages/platform/api/src/sources/http.ts+6−6
@@ -6,8 +6,8 @@ import { · 5 unmodified lines
66 import type {
77 ExecutionInteraction,
88 Source,
9− WorkspaceId,
10− WorkspaceOauthClient,
9+ ScopeId as WorkspaceId,
10+ ScopeOauthClient as WorkspaceOauthClient,
1111 } from "@executor/platform-sdk/schema";
1212 import type {
1313 ExecutorMcpSourceInput,
@@ -18,7 +18,7 @@ import * as Effect from "effect/Effect"; · 4 unmodified lines
1818 import {
1919 sourceAdapterRequiresInteractiveConnect,
2020 } from "@executor/platform-sdk/runtime";
21−import { WorkspaceOauthClientIdSchema } from "@executor/platform-sdk/schema";
21+import { ScopeOauthClientIdSchema as WorkspaceOauthClientIdSchema } from "@executor/platform-sdk/schema";
2222
2323 import {
2424 ControlPlaneBadRequestError,
@@ -130,8 +130,8 @@ const toExecutorSourceInput = ( · 105 unmodified lines
130130 version: payload.version,
131131 discoveryUrl: payload.discoveryUrl,
132132 scopes: payload.scopes,
133− workspaceOauthClientId: toWorkspaceOauthClientId(
134− payload.workspaceOauthClientId,
133+ scopeOauthClientId: toWorkspaceOauthClientId(
134+ payload.scopeOauthClientId,
135135 ),
136136 oauthClient: payload.oauthClient,
137137 name: payload.name,
@@ -1137,7 +1137,7 @@ export const ExecutorSourcesLive = HttpApiBuilder.group( · 999 unmodified lines
11371137 Effect.flatMap((executor) =>
11381138 Effect.gen(function* () {
11391139 const completed = yield* executor.effect.oauth.completeProviderCallback({
1140− workspaceId: path.workspaceId,
1140+ scopeId: path.workspaceId,
11411141 state: urlParams.state,
11421142 code: urlParams.code,
11431143 error: urlParams.error,
packages/platform/internal/src/tools/executor-admin.ts+103−103
@@ -1,9 +1,9 @@
11 import { toTool, type ToolMap } from "@executor/codemode-core";
22 import type { Executor } from "@executor/platform-sdk";
33 import {
4− type AccountId,
4+ type ScopeId as AccountId,
55 LocalInstallationSchema,
6− LocalWorkspacePolicySchema,
6+ LocalScopePolicySchema,
77 SourceDiscoveryResultSchema,
88 SourceIdSchema,
99 SourceInspectionDiscoverPayloadSchema,
@@ -11,8 +11,8 @@ import { · 1 unmodified line
1111 SourceInspectionSchema,
1212 SourceInspectionToolDetailSchema,
1313 SourceSchema,
14− type WorkspaceId,
15− WorkspaceOauthClientSchema,
14+ type ScopeId as WorkspaceId,
15+ ScopeOauthClientSchema,
1616 } from "@executor/platform-sdk/schema";
1717 import * as Effect from "effect/Effect";
1818 import * as Layer from "effect/Layer";
@@ -25,25 +25,25 @@ import { · 6 unmodified lines
2525 SecretMaterialStorerService,
2626 SecretMaterialUpdaterService,
2727 type ExecutorStateStoreShape,
28− type WorkspaceInternalToolContext,
28+ type ScopeInternalToolContext as WorkspaceInternalToolContext,
2929 RuntimeSourceAuthService,
3030 RuntimeSourceCatalogSyncService,
3131 RuntimeSourceStore,
3232 RuntimeSourceStoreService,
3333 } from "@executor/platform-sdk/runtime";
3434 import {
35− type RuntimeLocalWorkspaceState,
36− provideOptionalRuntimeLocalWorkspace,
37−} from "../../../sdk/src/runtime/workspace/runtime-context";
35+ type RuntimeLocalScopeState,
36+ provideOptionalRuntimeLocalScope,
37+} from "../../../sdk/src/runtime/scope/runtime-context";
3838 import {
3939 SourceArtifactStore,
4040 type SourceArtifactStoreShape,
41− WorkspaceConfigStore,
42− type WorkspaceConfigStoreShape,
43− WorkspaceStateStore,
44− type WorkspaceStateStoreShape,
45− makeWorkspaceStorageLayer,
46−} from "../../../sdk/src/runtime/workspace/storage";
41+ ScopeConfigStore,
42+ type ScopeConfigStoreShape,
43+ ScopeStateStore,
44+ type ScopeStateStoreShape,
45+ makeScopeStorageLayer,
46+} from "../../../sdk/src/runtime/scope/storage";
4747 import {
4848 CreateSecretPayloadSchema,
4949 CreateSecretResultSchema,
@@ -81,9 +81,9 @@ import { · 31 unmodified lines
8181 UpdatePolicyPayloadSchema,
8282 } from "@executor/platform-sdk/contracts";
8383 import {
84− CreateWorkspaceOauthClientPayloadSchema,
84+ CreateScopeOauthClientPayloadSchema,
8585 DiscoverSourcePayloadSchema,
86− type CreateWorkspaceOauthClientPayload,
86+ type CreateScopeOauthClientPayload as CreateWorkspaceOauthClientPayload,
8787 UpdateSourcePayloadSchema,
8888 } from "@executor/platform-sdk/contracts";
8989
@@ -145,7 +145,7 @@ const inspectDiscoverInputSchema = Schema.standardSchemaV1( · 55 unmodified lines
145145 }),
146146 );
147147 const listPoliciesOutputSchema = Schema.standardSchemaV1(
148− Schema.Array(LocalWorkspacePolicySchema),
148+ Schema.Array(LocalScopePolicySchema),
149149 );
150150 const policyIdInputSchema = Schema.standardSchemaV1(
151151 Schema.Struct({
@@ -167,10 +167,10 @@ const workspaceOauthClientListInputSchema = Schema.standardSchemaV1( · 15 unmodified lines
167167 }),
168168 );
169169 const workspaceOauthClientListOutputSchema = Schema.standardSchemaV1(
170− Schema.Array(WorkspaceOauthClientSchema),
170+ Schema.Array(ScopeOauthClientSchema),
171171 );
172172 const createWorkspaceOauthClientInputSchema = Schema.standardSchemaV1(
173− CreateWorkspaceOauthClientPayloadSchema,
173+ CreateScopeOauthClientPayloadSchema,
174174 );
175175 const removeWorkspaceOauthClientInputSchema = Schema.standardSchemaV1(
176176 Schema.Struct({
@@ -195,8 +195,8 @@ const sourceInspectionDiscoverOutputSchema = Schema.standardSchemaV1( · 18 unmodified lines
195195 const sourceDiscoveryOutputSchema = Schema.standardSchemaV1(
196196 SourceDiscoveryResultSchema,
197197 );
198−const localWorkspacePolicyOutputSchema = Schema.standardSchemaV1(
199− LocalWorkspacePolicySchema,
198+const localScopePolicyOutputSchema = Schema.standardSchemaV1(
199+ LocalScopePolicySchema,
200200 );
201201
202202 const makeRuntimeLayer = (input: {
@@ -205,8 +205,8 @@ const makeRuntimeLayer = (input: { · 2 unmodified lines
205205 sourceCatalogSyncService: Effect.Effect.Success<
206206 typeof RuntimeSourceCatalogSyncService
207207 >;
208− workspaceConfigStore: WorkspaceConfigStoreShape;
209− workspaceStateStore: WorkspaceStateStoreShape;
208+ scopeConfigStore: ScopeConfigStoreShape;
209+ scopeStateStore: ScopeStateStoreShape;
210210 sourceArtifactStore: SourceArtifactStoreShape;
211211 instanceConfigResolver: WorkspaceInternalToolContext["instanceConfigResolver"];
212212 storeSecretMaterial: WorkspaceInternalToolContext["storeSecretMaterial"];
@@ -224,9 +224,9 @@ const makeRuntimeLayer = (input: { · 11 unmodified lines
224224 Layer.succeed(SecretMaterialStorerService, input.storeSecretMaterial),
225225 Layer.succeed(SecretMaterialDeleterService, input.deleteSecretMaterial),
226226 Layer.succeed(SecretMaterialUpdaterService, input.updateSecretMaterial),
227− makeWorkspaceStorageLayer({
228− workspaceConfigStore: input.workspaceConfigStore,
229− workspaceStateStore: input.workspaceStateStore,
227+ makeScopeStorageLayer({
228+ scopeConfigStore: input.scopeConfigStore,
229+ scopeStateStore: input.scopeStateStore,
230230 sourceArtifactStore: input.sourceArtifactStore,
231231 }),
232232 );
@@ -241,34 +241,34 @@ const runRuntimeEffect = <A, E, R>(input: { · 8 unmodified lines
241241 | SecretMaterialStorerService
242242 | SecretMaterialDeleterService
243243 | SecretMaterialUpdaterService
244− | WorkspaceConfigStore
245− | WorkspaceStateStore
244+ | ScopeConfigStore
245+ | ScopeStateStore
246246 | SourceArtifactStore,
247247 never,
248248 never
249249 >;
250− runtimeLocalWorkspace: RuntimeLocalWorkspaceState | null;
250+ runtimeLocalScope: RuntimeLocalScopeState | null;
251251 }) =>
252252 Effect.runPromise(
253− provideOptionalRuntimeLocalWorkspace(
253+ provideOptionalRuntimeLocalScope(
254254 input.effect.pipe(Effect.provide(input.runtimeLayer)),
255− input.runtimeLocalWorkspace,
255+ input.runtimeLocalScope,
256256 ) as Effect.Effect<A, E, never>,
257257 );
258258
259−const runWorkspaceStorageEffect = <A, E, R>(input: {
259+const runScopeStorageEffect = <A, E, R>(input: {
260260 effect: Effect.Effect<A, E, R>;
261− workspaceStorageLayer: Layer.Layer<
262− WorkspaceConfigStore | WorkspaceStateStore | SourceArtifactStore,
261+ scopeStorageLayer: Layer.Layer<
262+ ScopeConfigStore | ScopeStateStore | SourceArtifactStore,
263263 never,
264264 never
265265 >;
266− runtimeLocalWorkspace: RuntimeLocalWorkspaceState | null;
266+ runtimeLocalScope: RuntimeLocalScopeState | null;
267267 }) =>
268268 Effect.runPromise(
269− provideOptionalRuntimeLocalWorkspace(
270− input.effect.pipe(Effect.provide(input.workspaceStorageLayer)),
271− input.runtimeLocalWorkspace,
269+ provideOptionalRuntimeLocalScope(
270+ input.effect.pipe(Effect.provide(input.scopeStorageLayer)),
271+ input.runtimeLocalScope,
272272 ) as Effect.Effect<A, E, never>,
273273 );
274274
@@ -279,17 +279,17 @@ export const createWorkspaceExecutorAdminToolMap = ( · 4 unmodified lines
279279 executorStateStore: input.executorStateStore,
280280 sourceStore: input.sourceStore,
281281 sourceCatalogSyncService: input.sourceCatalogSyncService,
282− workspaceConfigStore: input.workspaceConfigStore,
283− workspaceStateStore: input.workspaceStateStore,
282+ scopeConfigStore: input.scopeConfigStore,
283+ scopeStateStore: input.scopeStateStore,
284284 sourceArtifactStore: input.sourceArtifactStore,
285285 instanceConfigResolver: input.instanceConfigResolver,
286286 storeSecretMaterial: input.storeSecretMaterial,
287287 deleteSecretMaterial: input.deleteSecretMaterial,
288288 updateSecretMaterial: input.updateSecretMaterial,
289289 });
290− const workspaceStorageLayer = makeWorkspaceStorageLayer({
291− workspaceConfigStore: input.workspaceConfigStore,
292− workspaceStateStore: input.workspaceStateStore,
290+ const scopeStorageLayer = makeScopeStorageLayer({
291+ scopeConfigStore: input.scopeConfigStore,
292+ scopeStateStore: input.scopeStateStore,
293293 sourceArtifactStore: input.sourceArtifactStore,
294294 });
295295
@@ -306,8 +306,8 @@ export const createWorkspaceExecutorAdminToolMap = ( · 10 unmodified lines
306306 inputSchema: emptyInputSchema,
307307 outputSchema: localInstallationOutputSchema,
308308 execute: async () => ({
309− workspaceId: input.workspaceId,
310− accountId: input.accountId,
309+ scopeId: input.scopeId,
310+ actorScopeId: input.actorScopeId,
311311 }),
312312 },
313313 metadata,
@@ -332,7 +332,7 @@ export const createWorkspaceExecutorAdminToolMap = ( · 18 unmodified lines
332332 runRuntimeEffect({
333333 effect: listLocalSecrets(),
334334 runtimeLayer,
335− runtimeLocalWorkspace: input.runtimeLocalWorkspace,
335+ runtimeLocalScope: input.runtimeLocalScope,
336336 }),
337337 },
338338 metadata,
@@ -347,7 +347,7 @@ export const createWorkspaceExecutorAdminToolMap = ( · 8 unmodified lines
347347 runRuntimeEffect({
348348 effect: createLocalSecret(payload),
349349 runtimeLayer,
350− runtimeLocalWorkspace: input.runtimeLocalWorkspace,
350+ runtimeLocalScope: input.runtimeLocalScope,
351351 }),
352352 },
353353 metadata,
@@ -365,7 +365,7 @@ export const createWorkspaceExecutorAdminToolMap = ( · 11 unmodified lines
365365 runRuntimeEffect({
366366 effect: updateLocalSecret(payload),
367367 runtimeLayer,
368− runtimeLocalWorkspace: input.runtimeLocalWorkspace,
368+ runtimeLocalScope: input.runtimeLocalScope,
369369 }),
370370 },
371371 metadata,
@@ -379,7 +379,7 @@ export const createWorkspaceExecutorAdminToolMap = ( · 7 unmodified lines
379379 runRuntimeEffect({
380380 effect: deleteLocalSecret(secretId),
381381 runtimeLayer,
382− runtimeLocalWorkspace: input.runtimeLocalWorkspace,
382+ runtimeLocalScope: input.runtimeLocalScope,
383383 }),
384384 },
385385 metadata,
@@ -408,11 +408,11 @@ export const createWorkspaceExecutorAdminToolMap = ( · 22 unmodified lines
408408 execute: () =>
409409 runRuntimeEffect({
410410 effect: listSources({
411− workspaceId: input.workspaceId,
412− accountId: input.accountId as never,
411+ scopeId: input.scopeId,
412+ actorScopeId: input.actorScopeId as never,
413413 }),
414414 runtimeLayer,
415− runtimeLocalWorkspace: input.runtimeLocalWorkspace,
415+ runtimeLocalScope: input.runtimeLocalScope,
416416 }),
417417 },
418418 metadata,
@@ -425,12 +425,12 @@ export const createWorkspaceExecutorAdminToolMap = ( · 6 unmodified lines
425425 execute: ({ sourceId }: { sourceId: string }) =>
426426 runRuntimeEffect({
427427 effect: getSource({
428− workspaceId: input.workspaceId,
428+ scopeId: input.scopeId,
429429 sourceId: sourceId as never,
430− accountId: input.accountId as never,
430+ actorScopeId: input.actorScopeId as never,
431431 }),
432432 runtimeLayer,
433− runtimeLocalWorkspace: input.runtimeLocalWorkspace,
433+ runtimeLocalScope: input.runtimeLocalScope,
434434 }),
435435 },
436436 metadata,
@@ -446,13 +446,13 @@ export const createWorkspaceExecutorAdminToolMap = ( · 9 unmodified lines
446446 }) =>
447447 runRuntimeEffect({
448448 effect: updateSource({
449− workspaceId: input.workspaceId,
449+ scopeId: input.scopeId,
450450 sourceId: payload.sourceId as never,
451− accountId: input.accountId as never,
451+ actorScopeId: input.actorScopeId as never,
452452 payload: payload.payload as never,
453453 }),
454454 runtimeLayer,
455− runtimeLocalWorkspace: input.runtimeLocalWorkspace,
455+ runtimeLocalScope: input.runtimeLocalScope,
456456 }),
457457 },
458458 metadata,
@@ -465,11 +465,11 @@ export const createWorkspaceExecutorAdminToolMap = ( · 6 unmodified lines
465465 execute: ({ sourceId }: { sourceId: string }) =>
466466 runRuntimeEffect({
467467 effect: removeSource({
468− workspaceId: input.workspaceId,
468+ scopeId: input.scopeId,
469469 sourceId: sourceId as never,
470470 }),
471471 runtimeLayer,
472− runtimeLocalWorkspace: input.runtimeLocalWorkspace,
472+ runtimeLocalScope: input.runtimeLocalScope,
473473 }),
474474 },
475475 metadata,
@@ -482,11 +482,11 @@ export const createWorkspaceExecutorAdminToolMap = ( · 6 unmodified lines
482482 execute: ({ sourceId }: { sourceId: string }) =>
483483 runRuntimeEffect({
484484 effect: getSourceInspection({
485− workspaceId: input.workspaceId,
485+ scopeId: input.scopeId,
486486 sourceId: sourceId as never,
487487 }),
488488 runtimeLayer,
489− runtimeLocalWorkspace: input.runtimeLocalWorkspace,
489+ runtimeLocalScope: input.runtimeLocalScope,
490490 }),
491491 },
492492 metadata,
@@ -505,12 +505,12 @@ export const createWorkspaceExecutorAdminToolMap = ( · 12 unmodified lines
505505 }) =>
506506 runRuntimeEffect({
507507 effect: getSourceInspectionToolDetail({
508− workspaceId: input.workspaceId,
508+ scopeId: input.scopeId,
509509 sourceId: sourceId as never,
510510 toolPath,
511511 }),
512512 runtimeLayer,
513− runtimeLocalWorkspace: input.runtimeLocalWorkspace,
513+ runtimeLocalScope: input.runtimeLocalScope,
514514 }),
515515 },
516516 metadata,
@@ -529,12 +529,12 @@ export const createWorkspaceExecutorAdminToolMap = ( · 12 unmodified lines
529529 }) =>
530530 runRuntimeEffect({
531531 effect: discoverSourceInspectionTools({
532− workspaceId: input.workspaceId,
532+ scopeId: input.scopeId,
533533 sourceId: sourceId as never,
534534 payload: payload as never,
535535 }),
536536 runtimeLayer,
537− runtimeLocalWorkspace: input.runtimeLocalWorkspace,
537+ runtimeLocalScope: input.runtimeLocalScope,
538538 }),
539539 },
540540 metadata,
@@ -545,13 +545,13 @@ export const createWorkspaceExecutorAdminToolMap = ( · 4 unmodified lines
545545 inputSchema: workspaceOauthClientListInputSchema,
546546 outputSchema: workspaceOauthClientListOutputSchema,
547547 execute: ({ providerKey }: { providerKey: string }) =>
548− runWorkspaceStorageEffect({
549− effect: input.sourceAuthService.listWorkspaceOauthClients({
550− workspaceId: input.workspaceId,
548+ runScopeStorageEffect({
549+ effect: input.sourceAuthService.listScopeOauthClients({
550+ scopeId: input.scopeId,
551551 providerKey,
552552 }),
553− workspaceStorageLayer,
554− runtimeLocalWorkspace: input.runtimeLocalWorkspace,
553+ scopeStorageLayer,
554+ runtimeLocalScope: input.runtimeLocalScope,
555555 }),
556556 },
557557 metadata,
@@ -561,17 +561,17 @@ export const createWorkspaceExecutorAdminToolMap = ( · 3 unmodified lines
561561 description:
562562 "Create a workspace OAuth client used for shared provider auth flows.",
563563 inputSchema: createWorkspaceOauthClientInputSchema,
564− outputSchema: Schema.standardSchemaV1(WorkspaceOauthClientSchema),
564+ outputSchema: Schema.standardSchemaV1(ScopeOauthClientSchema),
565565 execute: (payload: CreateWorkspaceOauthClientPayload) =>
566− runWorkspaceStorageEffect({
567− effect: input.sourceAuthService.createWorkspaceOauthClient({
568− workspaceId: input.workspaceId,
566+ runScopeStorageEffect({
567+ effect: input.sourceAuthService.createScopeOauthClient({
568+ scopeId: input.scopeId,
569569 providerKey: payload.providerKey,
570570 label: payload.label,
571571 oauthClient: payload.oauthClient,
572572 }),
573− workspaceStorageLayer,
574− runtimeLocalWorkspace: input.runtimeLocalWorkspace,
573+ scopeStorageLayer,
574+ runtimeLocalScope: input.runtimeLocalScope,
575575 }),
576576 },
577577 metadata,
@@ -582,15 +582,15 @@ export const createWorkspaceExecutorAdminToolMap = ( · 4 unmodified lines
582582 inputSchema: removeWorkspaceOauthClientInputSchema,
583583 outputSchema: removeResultSchema,
584584 execute: ({ oauthClientId }: { oauthClientId: string }) =>
585− runWorkspaceStorageEffect({
585+ runScopeStorageEffect({
586586 effect: input.sourceAuthService
587− .removeWorkspaceOauthClient({
588− workspaceId: input.workspaceId,
587+ .removeScopeOauthClient({
588+ scopeId: input.scopeId,
589589 oauthClientId: oauthClientId as never,
590590 })
591591 .pipe(Effect.map((removed) => ({ removed }))),
592− workspaceStorageLayer,
593− runtimeLocalWorkspace: input.runtimeLocalWorkspace,
592+ scopeStorageLayer,
593+ runtimeLocalScope: input.runtimeLocalScope,
594594 }),
595595 },
596596 metadata,
@@ -602,15 +602,15 @@ export const createWorkspaceExecutorAdminToolMap = ( · 5 unmodified lines
602602 inputSchema: removeProviderGrantInputSchema,
603603 outputSchema: removeResultSchema,
604604 execute: ({ grantId }: { grantId: string }) =>
605− runWorkspaceStorageEffect({
605+ runScopeStorageEffect({
606606 effect: input.sourceAuthService
607607 .removeProviderAuthGrant({
608− workspaceId: input.workspaceId,
608+ scopeId: input.scopeId,
609609 grantId: grantId as never,
610610 })
611611 .pipe(Effect.map((removed) => ({ removed }))),
612− workspaceStorageLayer,
613− runtimeLocalWorkspace: input.runtimeLocalWorkspace,
612+ scopeStorageLayer,
613+ runtimeLocalScope: input.runtimeLocalScope,
614614 }),
615615 },
616616 metadata,
@@ -622,9 +622,9 @@ export const createWorkspaceExecutorAdminToolMap = ( · 5 unmodified lines
622622 outputSchema: listPoliciesOutputSchema,
623623 execute: () =>
624624 runRuntimeEffect({
625− effect: listPolicies(input.workspaceId),
625+ effect: listPolicies(input.scopeId),
626626 runtimeLayer,
627− runtimeLocalWorkspace: input.runtimeLocalWorkspace,
627+ runtimeLocalScope: input.runtimeLocalScope,
628628 }),
629629 },
630630 metadata,
@@ -633,15 +633,15 @@ export const createWorkspaceExecutorAdminToolMap = ( · 2 unmodified lines
633633 tool: {
634634 description: "Create a local workspace policy.",
635635 inputSchema: createPolicyInputSchema,
636− outputSchema: localWorkspacePolicyOutputSchema,
636+ outputSchema: localScopePolicyOutputSchema,
637637 execute: (payload: CreatePolicyPayload) =>
638638 runRuntimeEffect({
639639 effect: createPolicy({
640− workspaceId: input.workspaceId,
640+ scopeId: input.scopeId,
641641 payload,
642642 }),
643643 runtimeLayer,
644− runtimeLocalWorkspace: input.runtimeLocalWorkspace,
644+ runtimeLocalScope: input.runtimeLocalScope,
645645 }),
646646 },
647647 metadata,
@@ -650,15 +650,15 @@ export const createWorkspaceExecutorAdminToolMap = ( · 2 unmodified lines
650650 tool: {
651651 description: "Get one local workspace policy by id.",
652652 inputSchema: policyIdInputSchema,
653− outputSchema: localWorkspacePolicyOutputSchema,
653+ outputSchema: localScopePolicyOutputSchema,
654654 execute: ({ policyId }: { policyId: string }) =>
655655 runRuntimeEffect({
656656 effect: getPolicy({
657− workspaceId: input.workspaceId,
657+ scopeId: input.scopeId,
658658 policyId: policyId as never,
659659 }),
660660 runtimeLayer,
661− runtimeLocalWorkspace: input.runtimeLocalWorkspace,
661+ runtimeLocalScope: input.runtimeLocalScope,
662662 }),
663663 },
664664 metadata,
@@ -667,7 +667,7 @@ export const createWorkspaceExecutorAdminToolMap = ( · 2 unmodified lines
667667 tool: {
668668 description: "Update a local workspace policy.",
669669 inputSchema: updatePolicyInputSchema,
670− outputSchema: localWorkspacePolicyOutputSchema,
670+ outputSchema: localScopePolicyOutputSchema,
671671 execute: ({
672672 policyId,
673673 payload,
@@ -677,12 +677,12 @@ export const createWorkspaceExecutorAdminToolMap = ( · 3 unmodified lines
677677 }) =>
678678 runRuntimeEffect({
679679 effect: updatePolicy({
680− workspaceId: input.workspaceId,
680+ scopeId: input.scopeId,
681681 policyId: policyId as never,
682682 payload,
683683 }),
684684 runtimeLayer,
685− runtimeLocalWorkspace: input.runtimeLocalWorkspace,
685+ runtimeLocalScope: input.runtimeLocalScope,
686686 }),
687687 },
688688 metadata,
@@ -695,11 +695,11 @@ export const createWorkspaceExecutorAdminToolMap = ( · 6 unmodified lines
695695 execute: ({ policyId }: { policyId: string }) =>
696696 runRuntimeEffect({
697697 effect: removePolicy({
698− workspaceId: input.workspaceId,
698+ scopeId: input.scopeId,
699699 policyId: policyId as never,
700700 }),
701701 runtimeLayer,
702− runtimeLocalWorkspace: input.runtimeLocalWorkspace,
702+ runtimeLocalScope: input.runtimeLocalScope,
703703 }),
704704 },
705705 metadata,
@@ -903,7 +903,7 @@ export const createExecutorAdminToolMap = (input: { · 197 unmodified lines
903903 description:
904904 "Create a workspace OAuth client for a provider-backed source flow.",
905905 inputSchema: createWorkspaceOauthClientInputSchema,
906− outputSchema: Schema.standardSchemaV1(WorkspaceOauthClientSchema),
906+ outputSchema: Schema.standardSchemaV1(ScopeOauthClientSchema),
907907 execute: (payload: CreateWorkspaceOauthClientPayload) =>
908908 input.executor.sources.oauthClients.create(payload),
909909 },
@@ -952,7 +952,7 @@ export const createExecutorAdminToolMap = (input: { · 42 unmodified lines
952952 tool: {
953953 description: "Create a local workspace policy.",
954954 inputSchema: createPolicyInputSchema,
955− outputSchema: localWorkspacePolicyOutputSchema,
955+ outputSchema: localScopePolicyOutputSchema,
956956 execute: (payload: CreatePolicyPayload) =>
957957 input.executor.policies.create(payload),
958958 },
@@ -962,7 +962,7 @@ export const createExecutorAdminToolMap = (input: { · 3 unmodified lines
962962 tool: {
963963 description: "Get one local workspace policy by id.",
964964 inputSchema: policyIdInputSchema,
965− outputSchema: localWorkspacePolicyOutputSchema,
965+ outputSchema: localScopePolicyOutputSchema,
966966 execute: ({ policyId }: { policyId: string }) =>
967967 input.executor.policies.get(policyId),
968968 },
@@ -972,7 +972,7 @@ export const createExecutorAdminToolMap = (input: { · 3 unmodified lines
972972 tool: {
973973 description: "Update a local workspace policy.",
974974 inputSchema: updatePolicyInputSchema,
975− outputSchema: localWorkspacePolicyOutputSchema,
975+ outputSchema: localScopePolicyOutputSchema,
976976 execute: ({
977977 policyId,
978978 payload,
packages/platform/sdk-file/src/config.test.ts+1−1
@@ -34,7 +34,7 @@ describe("local-config", () => { · 33 unmodified lines
3434 join(configDirectory, "executor.jsonc"),
3535 `{
3636 "runtime": "ses",
37− // local workspace config
37+ // local scope config
3838 "sources": {
3939 "github": {
4040 "kind": "openapi",
packages/platform/sdk-file/src/config.ts+5−5
@@ -16,8 +16,8 @@ import { · 15 unmodified lines
1616 } from "@executor/platform-sdk/schema";
1717 import * as Effect from "effect/Effect";
1818 import * as Schema from "effect/Schema";
19−import type { ExecutorWorkspaceDescriptor } from "@executor/platform-sdk";
20−import type { LoadedLocalExecutorConfig } from "../../sdk/src/runtime/workspace-config";
19+import type { ExecutorScopeDescriptor } from "@executor/platform-sdk";
20+import type { LoadedLocalExecutorConfig } from "../../sdk/src/runtime/scope-config";
2121 import {
2222 LocalExecutorConfigDecodeError,
2323 LocalFileSystemError,
@@ -36,7 +36,7 @@ export const encodeLocalExecutorConfig = (config: LocalExecutorConfig): string = · 12 unmodified lines
3636
3737 export const resolveConfigRelativePath = (input: {
3838 path: string;
39− workspaceRoot: string;
39+ scopeRoot: string;
4040 }): string => {
4141 const trimmed = input.path.trim();
4242 if (trimmed.startsWith("~/")) {
@@ -48,10 +48,10 @@ export const resolveConfigRelativePath = (input: { · 5 unmodified lines
4848 if (isAbsolute(trimmed)) {
4949 return trimmed;
5050 }
51− return resolve(input.workspaceRoot, trimmed);
51+ return resolve(input.scopeRoot, trimmed);
5252 };
5353
54−export type { LoadedLocalExecutorConfig } from "../../sdk/src/runtime/workspace-config";
54+export type { LoadedLocalExecutorConfig } from "../../sdk/src/runtime/scope-config";
5555
5656 const PROJECT_CONFIG_BASENAME = "executor.jsonc";
5757 const PROJECT_CONFIG_DIRECTORY = ".executor";
packages/platform/sdk-file/src/executor-state-store.test.ts+1−1
@@ -52,7 +52,7 @@ describe("local-executor-state-store", () => { · 51 unmodified lines
5252 authArtifacts: [],
5353 authLeases: [],
5454 sourceOauthClients: [],
55− workspaceOauthClients: [],
55+ scopeOauthClients: [],
5656 providerAuthGrants: [],
5757 sourceAuthSessions: [],
5858 secretMaterials: [],
packages/platform/sdk-file/src/executor-state-store.ts+95−95
@@ -20,10 +20,10 @@ import { · 19 unmodified lines
2020 type SecretMaterial,
2121 type SourceAuthSession,
2222 SourceAuthSessionSchema,
23− type WorkspaceOauthClient,
24− WorkspaceOauthClientSchema,
25− type WorkspaceSourceOauthClient,
26− WorkspaceSourceOauthClientSchema,
23+ type ScopeOauthClient,
24+ ScopeOauthClientSchema,
25+ type ScopedSourceOauthClient,
26+ ScopedSourceOauthClientSchema,
2727 } from "@executor/platform-sdk/schema";
2828 import * as Effect from "effect/Effect";
2929 import * as Option from "effect/Option";
@@ -43,8 +43,8 @@ const LocalExecutorStateSnapshotSchema = Schema.Struct({ · 13 unmodified lines
4343 version: Schema.Literal(LOCAL_EXECUTOR_STATE_VERSION),
4444 authArtifacts: Schema.Array(AuthArtifactSchema),
4545 authLeases: Schema.Array(AuthLeaseSchema),
46− sourceOauthClients: Schema.Array(WorkspaceSourceOauthClientSchema),
47− workspaceOauthClients: Schema.Array(WorkspaceOauthClientSchema),
46+ sourceOauthClients: Schema.Array(ScopedSourceOauthClientSchema),
47+ scopeOauthClients: Schema.Array(ScopeOauthClientSchema),
4848 providerAuthGrants: Schema.Array(ProviderAuthGrantSchema),
4949 sourceAuthSessions: Schema.Array(SourceAuthSessionSchema),
5050 secretMaterials: Schema.Array(SecretMaterialSchema),
@@ -69,7 +69,7 @@ const defaultLocalExecutorStateSnapshot = (): LocalExecutorStateSnapshot => ({ · 18 unmodified lines
6969 authArtifacts: [],
7070 authLeases: [],
7171 sourceOauthClients: [],
72− workspaceOauthClients: [],
72+ scopeOauthClients: [],
7373 providerAuthGrants: [],
7474 sourceAuthSessions: [],
7575 secretMaterials: [],
@@ -114,7 +114,7 @@ const localExecutorStatePath = ( · 38 unmodified lines
114114 join(
115115 context.homeStateDirectory,
116116 "workspaces",
117− deriveLocalInstallation(context).workspaceId,
117+ deriveLocalInstallation(context).scopeId,
118118 LOCAL_EXECUTOR_STATE_BASENAME,
119119 );
120120
@@ -230,9 +230,9 @@ const mergeAuthArtifacts = ( · 109 unmodified lines
230230 for (const artifact of imported) {
231231 merged.set(
232232 [
233− artifact.workspaceId,
233+ artifact.scopeId,
234234 artifact.sourceId,
235− artifact.actorAccountId ?? "",
235+ artifact.actorScopeId ?? "",
236236 artifact.slot,
237237 ].join("::"),
238238 cloneValue(artifact),
@@ -242,9 +242,9 @@ const mergeAuthArtifacts = ( · 3 unmodified lines
242242 for (const artifact of current) {
243243 merged.set(
244244 [
245− artifact.workspaceId,
245+ artifact.scopeId,
246246 artifact.sourceId,
247− artifact.actorAccountId ?? "",
247+ artifact.actorScopeId ?? "",
248248 artifact.slot,
249249 ].join("::"),
250250 cloneValue(artifact),
@@ -255,14 +255,14 @@ const mergeAuthArtifacts = ( · 4 unmodified lines
255255 };
256256
257257 const mergeSourceOauthClients = (
258− current: readonly WorkspaceSourceOauthClient[],
259− imported: readonly WorkspaceSourceOauthClient[],
260−): WorkspaceSourceOauthClient[] => {
261− const merged = new Map<string, WorkspaceSourceOauthClient>();
258+ current: readonly ScopedSourceOauthClient[],
259+ imported: readonly ScopedSourceOauthClient[],
260+): ScopedSourceOauthClient[] => {
261+ const merged = new Map<string, ScopedSourceOauthClient>();
262262
263263 for (const oauthClient of imported) {
264264 merged.set(
265− [oauthClient.workspaceId, oauthClient.sourceId, oauthClient.providerKey].join(
265+ [oauthClient.scopeId, oauthClient.sourceId, oauthClient.providerKey].join(
266266 "::",
267267 ),
268268 cloneValue(oauthClient),
@@ -271,7 +271,7 @@ const mergeSourceOauthClients = ( · 2 unmodified lines
271271
272272 for (const oauthClient of current) {
273273 merged.set(
274− [oauthClient.workspaceId, oauthClient.sourceId, oauthClient.providerKey].join(
274+ [oauthClient.scopeId, oauthClient.sourceId, oauthClient.providerKey].join(
275275 "::",
276276 ),
277277 cloneValue(oauthClient),
@@ -282,21 +282,21 @@ const mergeSourceOauthClients = ( · 4 unmodified lines
282282 };
283283
284284 const mergeWorkspaceOauthClients = (
285− current: readonly WorkspaceOauthClient[],
286− imported: readonly WorkspaceOauthClient[],
287−): WorkspaceOauthClient[] => {
288− const merged = new Map<string, WorkspaceOauthClient>();
285+ current: readonly ScopeOauthClient[],
286+ imported: readonly ScopeOauthClient[],
287+): ScopeOauthClient[] => {
288+ const merged = new Map<string, ScopeOauthClient>();
289289
290290 for (const oauthClient of imported) {
291291 merged.set(
292− [oauthClient.workspaceId, oauthClient.providerKey, oauthClient.id].join("::"),
292+ [oauthClient.scopeId, oauthClient.providerKey, oauthClient.id].join("::"),
293293 cloneValue(oauthClient),
294294 );
295295 }
296296
297297 for (const oauthClient of current) {
298298 merged.set(
299− [oauthClient.workspaceId, oauthClient.providerKey, oauthClient.id].join("::"),
299+ [oauthClient.scopeId, oauthClient.providerKey, oauthClient.id].join("::"),
300300 cloneValue(oauthClient),
301301 );
302302 }
@@ -326,9 +326,9 @@ export const mergeImportedLocalExecutorStateSnapshot = (input: { · 23 unmodified lines
326326 input.current.sourceOauthClients,
327327 input.imported.sourceOauthClients ?? [],
328328 ),
329− workspaceOauthClients: mergeWorkspaceOauthClients(
330− input.current.workspaceOauthClients,
331− input.imported.workspaceOauthClients ?? [],
329+ scopeOauthClients: mergeWorkspaceOauthClients(
330+ input.current.scopeOauthClients,
331+ input.imported.scopeOauthClients ?? [],
332332 ),
333333 providerAuthGrants: mergeProviderAuthGrants(
334334 input.current.providerAuthGrants,
@@ -440,40 +440,40 @@ export const createLocalExecutorStateStore = ( · 105 unmodified lines
440440
441441 return {
442442 authArtifacts: {
443− listByWorkspaceId: (workspaceId: AuthArtifact["workspaceId"]) =>
443+ listByScopeId: (scopeId: AuthArtifact["scopeId"]) =>
444444 stateManager.read((state) =>
445445 sortByUpdatedAtAndIdAsc(
446− state.authArtifacts.filter((artifact) => artifact.workspaceId === workspaceId),
446+ state.authArtifacts.filter((artifact) => artifact.scopeId === scopeId),
447447 ),
448448 ),
449449
450− listByWorkspaceAndSourceId: (input: {
451− workspaceId: AuthArtifact["workspaceId"];
450+ listByScopeAndSourceId: (input: {
451+ scopeId: AuthArtifact["scopeId"];
452452 sourceId: AuthArtifact["sourceId"];
453453 }) =>
454454 stateManager.read((state) =>
455455 sortByUpdatedAtAndIdAsc(
456456 state.authArtifacts.filter(
457457 (artifact) =>
458− artifact.workspaceId === input.workspaceId
458+ artifact.scopeId === input.scopeId
459459 && artifact.sourceId === input.sourceId,
460460 ),
461461 ),
462462 ),
463463
464− getByWorkspaceSourceAndActor: (input: {
465− workspaceId: AuthArtifact["workspaceId"];
464+ getByScopeSourceAndActor: (input: {
465+ scopeId: AuthArtifact["scopeId"];
466466 sourceId: AuthArtifact["sourceId"];
467− actorAccountId: AuthArtifact["actorAccountId"];
467+ actorScopeId: AuthArtifact["actorScopeId"];
468468 slot: AuthArtifact["slot"];
469469 }) =>
470470 stateManager.read((state) => {
471471 const artifact = state.authArtifacts.find(
472472 (candidate) =>
473− candidate.workspaceId === input.workspaceId
473+ candidate.scopeId === input.scopeId
474474 && candidate.sourceId === input.sourceId
475475 && candidate.slot === input.slot
476− && actorEquals(candidate.actorAccountId, input.actorAccountId),
476+ && actorEquals(candidate.actorScopeId, input.actorScopeId),
477477 );
478478
479479 return artifact ? Option.some(cloneValue(artifact)) : Option.none<AuthArtifact>();
@@ -484,10 +484,10 @@ export const createLocalExecutorStateStore = ( · 4 unmodified lines
484484 const nextArtifacts = state.authArtifacts.filter(
485485 (candidate) =>
486486 !(
487− candidate.workspaceId === artifact.workspaceId
487+ candidate.scopeId === artifact.scopeId
488488 && candidate.sourceId === artifact.sourceId
489489 && candidate.slot === artifact.slot
490− && actorEquals(candidate.actorAccountId, artifact.actorAccountId)
490+ && actorEquals(candidate.actorScopeId, artifact.actorScopeId)
491491 ),
492492 );
493493 nextArtifacts.push(cloneValue(artifact));
@@ -501,18 +501,18 @@ export const createLocalExecutorStateStore = ( · 7 unmodified lines
501501 } satisfies StateMutationResult<void>;
502502 }),
503503
504− removeByWorkspaceSourceAndActor: (input: {
505− workspaceId: AuthArtifact["workspaceId"];
504+ removeByScopeSourceAndActor: (input: {
505+ scopeId: AuthArtifact["scopeId"];
506506 sourceId: AuthArtifact["sourceId"];
507− actorAccountId: AuthArtifact["actorAccountId"];
507+ actorScopeId: AuthArtifact["actorScopeId"];
508508 slot?: AuthArtifact["slot"];
509509 }) =>
510510 stateManager.mutate((state) => {
511511 const nextArtifacts = state.authArtifacts.filter(
512512 (candidate) =>
513− candidate.workspaceId !== input.workspaceId
513+ candidate.scopeId !== input.scopeId
514514 || candidate.sourceId !== input.sourceId
515− || !actorEquals(candidate.actorAccountId, input.actorAccountId)
515+ || !actorEquals(candidate.actorScopeId, input.actorScopeId)
516516 || (input.slot !== undefined && candidate.slot !== input.slot),
517517 );
518518
@@ -525,14 +525,14 @@ export const createLocalExecutorStateStore = ( · 6 unmodified lines
525525 } satisfies StateMutationResult<boolean>;
526526 }),
527527
528− removeByWorkspaceAndSourceId: (input: {
529− workspaceId: AuthArtifact["workspaceId"];
528+ removeByScopeAndSourceId: (input: {
529+ scopeId: AuthArtifact["scopeId"];
530530 sourceId: AuthArtifact["sourceId"];
531531 }) =>
532532 stateManager.mutate((state) => {
533533 const nextArtifacts = state.authArtifacts.filter(
534534 (candidate) =>
535− candidate.workspaceId !== input.workspaceId
535+ candidate.scopeId !== input.scopeId
536536 || candidate.sourceId !== input.sourceId,
537537 );
538538
@@ -591,30 +591,30 @@ export const createLocalExecutorStateStore = ( · 52 unmodified lines
591591 },
592592
593593 sourceOauthClients: {
594− getByWorkspaceSourceAndProvider: (input: {
595− workspaceId: WorkspaceSourceOauthClient["workspaceId"];
596− sourceId: WorkspaceSourceOauthClient["sourceId"];
594+ getByScopeSourceAndProvider: (input: {
595+ scopeId: ScopedSourceOauthClient["scopeId"];
596+ sourceId: ScopedSourceOauthClient["sourceId"];
597597 providerKey: string;
598598 }) =>
599599 stateManager.read((state) => {
600600 const oauthClient = state.sourceOauthClients.find(
601601 (candidate) =>
602− candidate.workspaceId === input.workspaceId
602+ candidate.scopeId === input.scopeId
603603 && candidate.sourceId === input.sourceId
604604 && candidate.providerKey === input.providerKey,
605605 );
606606
607607 return oauthClient
608608 ? Option.some(cloneValue(oauthClient))
609− : Option.none<WorkspaceSourceOauthClient>();
609+ : Option.none<ScopedSourceOauthClient>();
610610 }),
611611
612− upsert: (oauthClient: WorkspaceSourceOauthClient) =>
612+ upsert: (oauthClient: ScopedSourceOauthClient) =>
613613 stateManager.mutate((state) => {
614614 const nextOauthClients = state.sourceOauthClients.filter(
615615 (candidate) =>
616616 !(
617− candidate.workspaceId === oauthClient.workspaceId
617+ candidate.scopeId === oauthClient.scopeId
618618 && candidate.sourceId === oauthClient.sourceId
619619 && candidate.providerKey === oauthClient.providerKey
620620 ),
@@ -630,14 +630,14 @@ export const createLocalExecutorStateStore = ( · 9 unmodified lines
630630 } satisfies StateMutationResult<void>;
631631 }),
632632
633− removeByWorkspaceAndSourceId: (input: {
634− workspaceId: WorkspaceSourceOauthClient["workspaceId"];
635− sourceId: WorkspaceSourceOauthClient["sourceId"];
633+ removeByScopeAndSourceId: (input: {
634+ scopeId: ScopedSourceOauthClient["scopeId"];
635+ sourceId: ScopedSourceOauthClient["sourceId"];
636636 }) =>
637637 stateManager.mutate((state) => {
638638 const nextOauthClients = state.sourceOauthClients.filter(
639639 (candidate) =>
640− candidate.workspaceId !== input.workspaceId
640+ candidate.scopeId !== input.scopeId
641641 || candidate.sourceId !== input.sourceId,
642642 );
643643
@@ -651,35 +651,35 @@ export const createLocalExecutorStateStore = ( · 7 unmodified lines
651651 }),
652652 },
653653
654− workspaceOauthClients: {
655− listByWorkspaceAndProvider: (input: {
656− workspaceId: WorkspaceOauthClient["workspaceId"];
654+ scopeOauthClients: {
655+ listByScopeAndProvider: (input: {
656+ scopeId: ScopeOauthClient["scopeId"];
657657 providerKey: string;
658658 }) =>
659659 stateManager.read((state) =>
660660 sortByUpdatedAtAndIdAsc(
661− state.workspaceOauthClients.filter(
661+ state.scopeOauthClients.filter(
662662 (candidate) =>
663− candidate.workspaceId === input.workspaceId
663+ candidate.scopeId === input.scopeId
664664 && candidate.providerKey === input.providerKey,
665665 ),
666666 ),
667667 ),
668668
669− getById: (id: WorkspaceOauthClient["id"]) =>
669+ getById: (id: ScopeOauthClient["id"]) =>
670670 stateManager.read((state) => {
671− const oauthClient = state.workspaceOauthClients.find(
671+ const oauthClient = state.scopeOauthClients.find(
672672 (candidate) => candidate.id === id,
673673 );
674674
675675 return oauthClient
676676 ? Option.some(cloneValue(oauthClient))
677− : Option.none<WorkspaceOauthClient>();
677+ : Option.none<ScopeOauthClient>();
678678 }),
679679
680− upsert: (oauthClient: WorkspaceOauthClient) =>
680+ upsert: (oauthClient: ScopeOauthClient) =>
681681 stateManager.mutate((state) => {
682− const nextOauthClients = state.workspaceOauthClients.filter(
682+ const nextOauthClients = state.scopeOauthClients.filter(
683683 (candidate) => candidate.id !== oauthClient.id,
684684 );
685685 nextOauthClients.push(cloneValue(oauthClient));
@@ -687,50 +687,50 @@ export const createLocalExecutorStateStore = ( · 1 unmodified line
687687 return {
688688 state: {
689689 ...state,
690− workspaceOauthClients: nextOauthClients,
690+ scopeOauthClients: nextOauthClients,
691691 },
692692 value: undefined,
693693 } satisfies StateMutationResult<void>;
694694 }),
695695
696− removeById: (id: WorkspaceOauthClient["id"]) =>
696+ removeById: (id: ScopeOauthClient["id"]) =>
697697 stateManager.mutate((state) => {
698− const nextOauthClients = state.workspaceOauthClients.filter(
698+ const nextOauthClients = state.scopeOauthClients.filter(
699699 (candidate) => candidate.id !== id,
700700 );
701701
702702 return {
703703 state: {
704704 ...state,
705− workspaceOauthClients: nextOauthClients,
705+ scopeOauthClients: nextOauthClients,
706706 },
707− value: nextOauthClients.length !== state.workspaceOauthClients.length,
707+ value: nextOauthClients.length !== state.scopeOauthClients.length,
708708 } satisfies StateMutationResult<boolean>;
709709 }),
710710 },
711711
712712 providerAuthGrants: {
713− listByWorkspaceId: (workspaceId: ProviderAuthGrant["workspaceId"]) =>
713+ listByScopeId: (scopeId: ProviderAuthGrant["scopeId"]) =>
714714 stateManager.read((state) =>
715715 sortByUpdatedAtAndIdAsc(
716716 state.providerAuthGrants.filter(
717− (grant) => grant.workspaceId === workspaceId,
717+ (grant) => grant.scopeId === scopeId,
718718 ),
719719 ),
720720 ),
721721
722− listByWorkspaceActorAndProvider: (input: {
723− workspaceId: ProviderAuthGrant["workspaceId"];
724− actorAccountId: ProviderAuthGrant["actorAccountId"];
722+ listByScopeActorAndProvider: (input: {
723+ scopeId: ProviderAuthGrant["scopeId"];
724+ actorScopeId: ProviderAuthGrant["actorScopeId"];
725725 providerKey: string;
726726 }) =>
727727 stateManager.read((state) =>
728728 sortByUpdatedAtAndIdAsc(
729729 state.providerAuthGrants.filter(
730730 (grant) =>
731− grant.workspaceId === input.workspaceId
731+ grant.scopeId === input.scopeId
732732 && grant.providerKey === input.providerKey
733− && actorEquals(grant.actorAccountId, input.actorAccountId),
733+ && actorEquals(grant.actorScopeId, input.actorScopeId),
734734 ),
735735 ),
736736 ),
@@ -782,11 +782,11 @@ export const createLocalExecutorStateStore = ( · 45 unmodified lines
782782 listAll: () =>
783783 stateManager.read((state) => sortByUpdatedAtAndIdAsc(state.sourceAuthSessions)),
784784
785− listByWorkspaceId: (workspaceId: SourceAuthSession["workspaceId"]) =>
785+ listByScopeId: (scopeId: SourceAuthSession["scopeId"]) =>
786786 stateManager.read((state) =>
787787 sortByUpdatedAtAndIdAsc(
788788 state.sourceAuthSessions.filter(
789− (session) => session.workspaceId === workspaceId,
789+ (session) => session.scopeId === scopeId,
790790 ),
791791 ),
792792 ),
@@ -811,20 +811,20 @@ export const createLocalExecutorStateStore = ( · 18 unmodified lines
811811 : Option.none<SourceAuthSession>();
812812 }),
813813
814− getPendingByWorkspaceSourceAndActor: (input: {
815− workspaceId: SourceAuthSession["workspaceId"];
814+ getPendingByScopeSourceAndActor: (input: {
815+ scopeId: SourceAuthSession["scopeId"];
816816 sourceId: SourceAuthSession["sourceId"];
817− actorAccountId: SourceAuthSession["actorAccountId"];
817+ actorScopeId: SourceAuthSession["actorScopeId"];
818818 credentialSlot?: SourceAuthSession["credentialSlot"];
819819 }) =>
820820 stateManager.read((state) => {
821821 const session = sortByUpdatedAtAndIdAsc(
822822 state.sourceAuthSessions.filter(
823823 (candidate) =>
824− candidate.workspaceId === input.workspaceId
824+ candidate.scopeId === input.scopeId
825825 && candidate.sourceId === input.sourceId
826826 && candidate.status === "pending"
827− && actorEquals(candidate.actorAccountId, input.actorAccountId)
827+ && actorEquals(candidate.actorScopeId, input.actorScopeId)
828828 && (input.credentialSlot === undefined
829829 || candidate.credentialSlot === input.credentialSlot),
830830 ),
@@ -846,7 +846,7 @@ export const createLocalExecutorStateStore = ( · 15 unmodified lines
846846
847847 update: (
848848 id: SourceAuthSession["id"],
849− patch: Partial<Omit<SourceAuthSession, "id" | "workspaceId" | "sourceId" | "createdAt">>,
849+ patch: Partial<Omit<SourceAuthSession, "id" | "scopeId" | "sourceId" | "createdAt">>,
850850 ) =>
851851 stateManager.mutate((state) => {
852852 let updated: SourceAuthSession | null = null;
@@ -887,14 +887,14 @@ export const createLocalExecutorStateStore = ( · 34 unmodified lines
887887 } satisfies StateMutationResult<void>;
888888 }),
889889
890− removeByWorkspaceAndSourceId: (
891− workspaceId: SourceAuthSession["workspaceId"],
890+ removeByScopeAndSourceId: (
891+ scopeId: SourceAuthSession["scopeId"],
892892 sourceId: SourceAuthSession["sourceId"],
893893 ) =>
894894 stateManager.mutate((state) => {
895895 const nextSessions = state.sourceAuthSessions.filter(
896896 (candidate) =>
897− candidate.workspaceId !== workspaceId || candidate.sourceId !== sourceId,
897+ candidate.scopeId !== scopeId || candidate.sourceId !== sourceId,
898898 );
899899
900900 return {
@@ -993,14 +993,14 @@ export const createLocalExecutorStateStore = ( · 92 unmodified lines
993993 : Option.none<Execution>();
994994 }),
995995
996− getByWorkspaceAndId: (
997− workspaceId: Execution["workspaceId"],
996+ getByScopeAndId: (
997+ scopeId: Execution["scopeId"],
998998 executionId: Execution["id"],
999999 ) =>
10001000 stateManager.read((state) => {
10011001 const execution = state.executions.find(
10021002 (candidate) =>
1003− candidate.workspaceId === workspaceId && candidate.id === executionId,
1003+ candidate.scopeId === scopeId && candidate.id === executionId,
10041004 );
10051005 return execution
10061006 ? Option.some(cloneValue(execution))
@@ -1019,7 +1019,7 @@ export const createLocalExecutorStateStore = ( · 12 unmodified lines
10191019 update: (
10201020 executionId: Execution["id"],
10211021 patch: Partial<
1022− Omit<Execution, "id" | "workspaceId" | "createdByAccountId" | "createdAt">
1022+ Omit<Execution, "id" | "scopeId" | "createdByAccountId" | "createdAt">
10231023 >,
10241024 ) =>
10251025 stateManager.mutate((state) => {
packages/platform/sdk-file/src/index.ts+8−8
@@ -10,7 +10,7 @@ import { · 9 unmodified lines
1010 type ExecutorBackendServices,
1111 type ExecutorLocalToolBackend,
1212 type ExecutorSourceTypeDeclarationsBackend,
13− type ExecutorWorkspaceDescriptor,
13+ type ExecutorScopeDescriptor,
1414 } from "@executor/platform-sdk";
1515 import type {
1616 LocalExecutorConfig,
@@ -198,11 +198,11 @@ const createBoundSourceTypeDeclarationsRefresher = ( · 181 unmodified lines
198198 }),
199199 });
200200
201−const toExecutorWorkspaceContext = (
201+const toExecutorScopeContext = (
202202 context: ResolvedLocalWorkspaceContext,
203−): ExecutorWorkspaceDescriptor => ({
204− workspaceName: context.workspaceName,
205− workspaceRoot: context.workspaceRoot,
203+): ExecutorScopeDescriptor => ({
204+ scopeName: context.workspaceName,
205+ scopeRoot: context.workspaceRoot,
206206 metadata: {
207207 kind: "file",
208208 configDirectory: context.configDirectory,
@@ -248,11 +248,11 @@ export const createLocalExecutorServicesEffect = ( · 39 unmodified lines
248248 });
249249
250250 return {
251− workspace: toExecutorWorkspaceContext(workspaceContext),
251+ scope: toExecutorScopeContext(workspaceContext),
252252 storage: {
253253 installation: createBoundInstallationStore(workspaceContext),
254− workspaceConfig: workspaceConfigStore,
255− workspaceState: createBoundWorkspaceStateStore(
254+ scopeConfig: workspaceConfigStore,
255+ scopeState: createBoundWorkspaceStateStore(
256256 workspaceContext,
257257 fileSystem,
258258 ),
packages/platform/sdk-file/src/installation.test.ts+4−4
@@ -42,8 +42,8 @@ describe("local-installation", () => { · 41 unmodified lines
4242 const installation = runtime.localInstallation;
4343 const fs = yield* FileSystem.FileSystem;
4444
45− expect(installation.accountId).toBe("acc_local_default");
46− expect(installation.workspaceId.startsWith("ws_local_")).toBe(true);
45+ expect(installation.actorScopeId).toBe("acc_local_default");
46+ expect(installation.scopeId.startsWith("ws_local_")).toBe(true);
4747 expect(yield* fs.exists(join(workspaceRoot, ".executor", "executor.jsonc"))).toBe(false);
4848 }).pipe(Effect.provide(NodeFileSystem.layer)),
4949 60_000,
@@ -63,8 +63,8 @@ describe("local-installation", () => { · 13 unmodified lines
6363 context,
6464 });
6565
66− expect(second.accountId).toBe(first.accountId);
67− expect(second.workspaceId).toBe(first.workspaceId);
66+ expect(second.actorScopeId).toBe(first.actorScopeId);
67+ expect(second.scopeId).toBe(first.scopeId);
6868 }).pipe(Effect.provide(NodeFileSystem.layer)),
6969 );
7070 });
packages/platform/sdk-file/src/installation.ts+9−6
@@ -1,15 +1,14 @@
11 import { createHash } from "node:crypto";
22
33 import {
4− AccountIdSchema,
5− WorkspaceIdSchema,
4+ ScopeIdSchema,
65 type LocalInstallation,
76 } from "@executor/platform-sdk/schema";
87 import * as Effect from "effect/Effect";
98
109 import type { ResolvedLocalWorkspaceContext } from "./config";
1110
12−const LOCAL_ACCOUNT_ID = AccountIdSchema.make("acc_local_default");
11+const LOCAL_ACCOUNT_ID = ScopeIdSchema.make("acc_local_default");
1312
1413 const stableHash = (value: string): string =>
1514 createHash("sha256").update(value).digest("hex").slice(0, 16);
@@ -18,15 +17,19 @@ const normalizeSlashPath = (value: string): string => · 2 unmodified lines
1817 value.replaceAll("\\", "/");
1918
2019 const deriveWorkspaceId = (context: ResolvedLocalWorkspaceContext) =>
21− WorkspaceIdSchema.make(
20+ ScopeIdSchema.make(
2221 `ws_local_${stableHash(normalizeSlashPath(context.workspaceRoot))}`,
2322 );
2423
2524 export const deriveLocalInstallation = (
2625 context: ResolvedLocalWorkspaceContext,
2726 ): LocalInstallation => ({
28− accountId: LOCAL_ACCOUNT_ID,
29− workspaceId: deriveWorkspaceId(context),
27+ actorScopeId: LOCAL_ACCOUNT_ID,
28+ scopeId: deriveWorkspaceId(context),
29+ resolutionScopeIds: [
30+ deriveWorkspaceId(context),
31+ LOCAL_ACCOUNT_ID,
32+ ],
3033 });
3134
3235 export const loadLocalInstallation = (
packages/platform/sdk-file/src/secret-material-providers.ts+7−7
@@ -28,9 +28,9 @@ import { · 27 unmodified lines
2828 type StoreSecretMaterial,
2929 type DeleteSecretMaterial,
3030 type UpdateSecretMaterial,
31−} from "../../sdk/src/runtime/workspace/secret-material-providers";
32−import { fromConfigSecretProviderId } from "../../sdk/src/runtime/workspace/config-secrets";
33−import { getRuntimeLocalWorkspaceOption } from "../../sdk/src/runtime/workspace/runtime-context";
31+} from "../../sdk/src/runtime/scope/secret-material-providers";
32+import { fromConfigSecretProviderId } from "../../sdk/src/runtime/scope/config-secrets";
33+import { getRuntimeLocalScopeOption } from "../../sdk/src/runtime/scope/runtime-context";
3434 import {
3535 ExecutorStateStore,
3636 type ExecutorStateStoreShape,
@@ -876,7 +876,7 @@ const readFileSecretValue = (input: { · 839 unmodified lines
876876 const fs = yield* FileSystem.FileSystem;
877877 const resolvedPath = resolveConfigRelativePath({
878878 path: input.provider.path,
879− workspaceRoot: input.workspaceRoot,
879+ scopeRoot: input.workspaceRoot,
880880 });
881881 const raw = yield* fs.readFileString(resolvedPath, "utf8").pipe(
882882 Effect.mapError(toError),
@@ -1140,14 +1140,14 @@ const resolveRuntimeSecretMaterialConfig = (input: { · 257 unmodified lines
11401140 workspaceRoot?: string | null;
11411141 }) =>
11421142 Effect.gen(function* () {
1143− const runtimeLocalWorkspace = yield* getRuntimeLocalWorkspaceOption();
1143+ const runtimeLocalScope = yield* getRuntimeLocalScopeOption();
11441144
11451145 return {
11461146 localConfig: input.localConfig
1147− ?? runtimeLocalWorkspace?.loadedConfig.config
1147+ ?? runtimeLocalScope?.loadedConfig.config
11481148 ?? null,
11491149 workspaceRoot: input.workspaceRoot
1150− ?? runtimeLocalWorkspace?.workspace.workspaceRoot
1150+ ?? runtimeLocalScope?.scope.scopeRoot
11511151 ?? null,
11521152 };
11531153 });
packages/platform/sdk-file/src/source-artifacts.test.ts+2−2
@@ -7,7 +7,7 @@ import * as Effect from "effect/Effect"; · 6 unmodified lines
77
88 import {
99 SourceIdSchema,
10− WorkspaceIdSchema,
10+ ScopeIdSchema,
1111 type Source,
1212 } from "@executor/platform-sdk/schema";
1313
@@ -52,7 +52,7 @@ const makeContext = (): Effect.Effect< · 38 unmodified lines
5252
5353 const makeSource = (): Source => ({
5454 id: SourceIdSchema.make("src_test"),
55− workspaceId: WorkspaceIdSchema.make("ws_test"),
55+ scopeId: ScopeIdSchema.make("ws_test"),
5656 name: "Test Source",
5757 kind: "openapi",
5858 endpoint: "https://example.com/api",
packages/platform/sdk-file/src/source-type-declarations.test.ts+2−3
@@ -7,7 +7,7 @@ import * as Effect from "effect/Effect"; · 6 unmodified lines
77
88 import {
99 SourceIdSchema,
10− WorkspaceIdSchema,
10+ ScopeIdSchema,
1111 type Source,
1212 } from "@executor/platform-sdk/schema";
1313
@@ -21,7 +21,6 @@ import { · 7 unmodified lines
2121 ExecutableIdSchema,
2222 ResourceIdSchema,
2323 ResponseSetIdSchema,
24− ScopeIdSchema,
2524 ShapeSymbolIdSchema,
2625 } from "@executor/ir/ids";
2726 import type { CatalogV1, ProvenanceRef } from "@executor/ir/model";
@@ -397,7 +396,7 @@ const makeSource = (input: { · 369 unmodified lines
397396 status?: Source["status"];
398397 }): Source => ({
399398 id: SourceIdSchema.make(input.id),
400− workspaceId: WorkspaceIdSchema.make("ws_test"),
399+ scopeId: ScopeIdSchema.make("ws_test"),
401400 name: input.id,
402401 kind: "graphql",
403402 endpoint: "https://api.linear.app/graphql",
packages/platform/sdk-file/src/workspace-state.ts+9−9
@@ -3,11 +3,11 @@ import { FileSystem } from "@effect/platform"; · 2 unmodified lines
33 import * as Effect from "effect/Effect";
44 import * as Schema from "effect/Schema";
55
6−import type { LocalWorkspaceState } from "../../sdk/src/runtime/workspace-state";
6+import type { LocalScopeState } from "../../sdk/src/runtime/scope-state";
77 import {
8− defaultLocalWorkspaceState,
9− LocalWorkspaceStateSchema,
10−} from "../../sdk/src/runtime/workspace-state";
8+ defaultLocalScopeState,
9+ LocalScopeStateSchema,
10+} from "../../sdk/src/runtime/scope-state";
1111 import type { ResolvedLocalWorkspaceContext } from "./config";
1212 import {
1313 LocalFileSystemError,
@@ -17,7 +17,7 @@ import { · 3 unmodified lines
1717
1818 const WORKSPACE_STATE_BASENAME = "workspace-state.json";
1919
20−const decodeLocalWorkspaceState = Schema.decodeUnknownSync(LocalWorkspaceStateSchema);
20+const decodeLocalWorkspaceState = Schema.decodeUnknownSync(LocalScopeStateSchema);
2121
2222 const mapFileSystemError = (path: string, action: string) => (cause: unknown) =>
2323 new LocalFileSystemError({
@@ -34,7 +34,7 @@ export const localWorkspaceStatePath = ( · 10 unmodified lines
3434 export const loadLocalWorkspaceState = (
3535 context: ResolvedLocalWorkspaceContext,
3636 ): Effect.Effect<
37− LocalWorkspaceState,
37+ LocalScopeState,
3838 LocalFileSystemError | LocalWorkspaceStateDecodeError,
3939 FileSystem.FileSystem
4040 > =>
@@ -45,7 +45,7 @@ export const loadLocalWorkspaceState = ( · 4 unmodified lines
4545 Effect.mapError(mapFileSystemError(path, "check workspace state path")),
4646 );
4747 if (!exists) {
48− return defaultLocalWorkspaceState();
48+ return defaultLocalScopeState();
4949 }
5050
5151 const content = yield* fs.readFileString(path, "utf8").pipe(
@@ -55,7 +55,7 @@ export const loadLocalWorkspaceState = ( · 3 unmodified lines
5555 try: () => decodeLocalWorkspaceState(JSON.parse(content) as unknown),
5656 catch: (cause) =>
5757 new LocalWorkspaceStateDecodeError({
58− message: `Invalid local workspace state at ${path}: ${unknownLocalErrorDetails(cause)}`,
58+ message: `Invalid local scope state at ${path}: ${unknownLocalErrorDetails(cause)}`,
5959 path,
6060 details: unknownLocalErrorDetails(cause),
6161 }),
@@ -64,7 +64,7 @@ export const loadLocalWorkspaceState = ( · 2 unmodified lines
6464
6565 export const writeLocalWorkspaceState = (input: {
6666 context: ResolvedLocalWorkspaceContext;
67− state: LocalWorkspaceState;
67+ state: LocalScopeState;
6868 }): Effect.Effect<void, LocalFileSystemError, FileSystem.FileSystem> =>
6969 Effect.gen(function* () {
7070 const fs = yield* FileSystem.FileSystem;
packages/platform/sdk/package.json+1−0
@@ -42,6 +42,7 @@ · 41 unmodified lines
4242 },
4343 "devDependencies": {
4444 "@effect/vitest": "catalog:",
45+ "@executor/effect-test-utils": "workspace:*",
4546 "@modelcontextprotocol/sdk": "^1.26.0",
4647 "@types/node": "catalog:",
4748 "bun-types": "catalog:",
packages/platform/sdk/src/backend.ts+14−14
@@ -6,8 +6,8 @@ import { · 5 unmodified lines
66 type BoundLocalToolRuntimeLoader,
77 type BoundSourceArtifactStore,
88 type BoundSourceTypeDeclarationsRefresher,
9− type BoundWorkspaceConfigStore,
10− type BoundWorkspaceStateStore,
9+ type BoundScopeConfigStore,
10+ type BoundScopeStateStore,
1111 type ExecutorRuntime,
1212 type ExecutorRuntimeOptions,
1313 type RuntimeInstanceConfigService,
@@ -15,13 +15,13 @@ import { · 1 unmodified line
1515 type RuntimeSecretMaterialServices,
1616 } from "./runtime";
1717 import type {
18− ExecutorWorkspaceContext,
19− ExecutorWorkspaceDescriptor,
20−} from "./workspace";
18+ ExecutorScopeContext,
19+ ExecutorScopeDescriptor,
20+} from "./scope";
2121 export type {
22− ExecutorWorkspaceContext,
23− ExecutorWorkspaceDescriptor,
24−} from "./workspace";
22+ ExecutorScopeContext,
23+ ExecutorScopeDescriptor,
24+} from "./scope";
2525
2626 export type ExecutorBackend = {
2727 createRuntime: (
@@ -30,8 +30,8 @@ export type ExecutorBackend = { · 2 unmodified lines
3030 };
3131
3232 export type ExecutorInstallationBackend = BoundInstallationStore;
33−export type ExecutorWorkspaceConfigBackend = BoundWorkspaceConfigStore;
34−export type ExecutorWorkspaceStateBackend = BoundWorkspaceStateStore;
33+export type ExecutorScopeConfigBackend = BoundScopeConfigStore;
34+export type ExecutorScopeStateBackend = BoundScopeStateStore;
3535 export type ExecutorSourceArtifactBackend = BoundSourceArtifactStore;
3636 export type ExecutorStateBackend = import("./runtime").ExecutorStateStoreShape;
3737 export type ExecutorLocalToolBackend = BoundLocalToolRuntimeLoader;
@@ -42,8 +42,8 @@ export type ExecutorInstanceConfigBackend = RuntimeInstanceConfigService; · 4 unmodified lines
4242
4343 export type ExecutorStorageBackend = {
4444 installation: ExecutorInstallationBackend;
45− workspaceConfig: ExecutorWorkspaceConfigBackend;
46− workspaceState: ExecutorWorkspaceStateBackend;
45+ scopeConfig: ExecutorScopeConfigBackend;
46+ scopeState: ExecutorScopeStateBackend;
4747 sourceArtifacts: ExecutorSourceArtifactBackend;
4848 executorState: ExecutorStateBackend;
4949 secretMaterial: ExecutorSecretMaterialBackend;
@@ -51,7 +51,7 @@ export type ExecutorStorageBackend = { · 1 unmodified line
5151 };
5252
5353 export type ExecutorBackendServices = {
54− workspace: ExecutorWorkspaceDescriptor;
54+ scope: ExecutorScopeDescriptor;
5555 storage: ExecutorStorageBackend;
5656 instanceConfig: ExecutorInstanceConfigBackend;
5757 localTools?: ExecutorLocalToolBackend;
@@ -68,7 +68,7 @@ export const createExecutorBackend = (input: { · 10 unmodified lines
6868 createExecutorRuntimeFromServices({
6969 ...options,
7070 services: {
71− workspace: services.workspace,
71+ scope: services.scope,
7272 storage: services.storage satisfies RuntimeStorageServices,
7373 localToolRuntimeLoader: services.localTools,
7474 sourceTypeDeclarationsRefresher: services.sourceTypeDeclarations,
packages/platform/sdk/src/errors.ts+3−1
@@ -1,4 +1,6 @@
1−import { HttpApiSchema } from "@effect/platform";
1+import {
2+ HttpApiSchema,
3+} from "@effect/platform";
24 import * as Schema from "effect/Schema";
35
46 export class ControlPlaneBadRequestError extends Schema.TaggedError<ControlPlaneBadRequestError>()(
packages/platform/sdk/src/executions/contracts.ts+3−1
@@ -1,6 +1,8 @@
11 import * as Schema from "effect/Schema";
22
3−import { TrimmedNonEmptyStringSchema } from "../string-schemas";
3+import {
4+ TrimmedNonEmptyStringSchema,
5+} from "../string-schemas";
46
57 export const CreateExecutionPayloadSchema = Schema.Struct({
68 code: TrimmedNonEmptyStringSchema,
packages/platform/sdk/src/executor.test.ts+453−0
@@ -0,0 +1,453 @@
1+import { join } from "node:path";
2+import {
3+ HttpApi,
4+ HttpApiEndpoint,
5+ HttpApiGroup,
6+ HttpApiSchema,
7+ HttpApiBuilder,
8+ HttpApiClient,
9+ FileSystem,
10+} from "@effect/platform";
11+import {
12+ NodeFileSystem,
13+ NodeHttpServer,
14+} from "@effect/platform-node";
15+import {
16+ describe,
17+ expect,
18+ it,
19+} from "@effect/vitest";
20+import * as Either from "effect/Either";
21+import {
22+ assertInclude,
23+} from "@effect/vitest/utils";
24+import * as Effect from "effect/Effect";
25+import * as Layer from "effect/Layer";
26+import * as Schema from "effect/Schema";
27+import {
28+ makeOpenApiTestServer,
29+} from "@executor/effect-test-utils";
30+
31+import {
32+ createExecutorBackend,
33+ createExecutorEffect,
34+ type Executor,
35+} from "./index";
36+import {
37+ type LocalInstallation,
38+ ScopeIdSchema,
39+} from "./schema";
40+import {
41+ createLocalExecutorServicesEffect,
42+} from "../../sdk-file/src/index";
43+import {
44+ ExecutorApi,
45+ createExecutorApiLayer,
46+} from "../../api/src/index";
47+
48+type LocalExecutorServices = Effect.Effect.Success<
49+ ReturnType<typeof createLocalExecutorServicesEffect>
50+>;
51+
52+const createClientLayer = (executor: Executor) =>
53+ HttpApiBuilder.serve().pipe(
54+ Layer.provide(createExecutorApiLayer(executor)),
55+ Layer.provideMerge(NodeHttpServer.layerTest),
56+ );
57+
58+const createExecutorApiClient = () =>
59+ HttpApiClient.make(ExecutorApi, {
60+ });
61+
62+type ExecutorApiClient = Effect.Effect.Success<
63+ ReturnType<typeof createExecutorApiClient>
64+>;
65+
66+const withExecutorApiClient = <A, E>(
67+ executor: Executor,
68+ f: (client: ExecutorApiClient) => Effect.Effect<A, E, never>,
69+): Effect.Effect<A, E, never> =>
70+ Effect.gen(function* () {
71+ const client = yield* createExecutorApiClient();
72+ return yield* f(client);
73+ }).pipe(Effect.provide(createClientLayer(executor).pipe(Layer.orDie)));
74+
75+const expectLeft = <A, E>(effect: Effect.Effect<A, E, never>) =>
76+ Effect.either(effect).pipe(
77+ Effect.flatMap((result) =>
78+ Either.isLeft(result)
79+ ? Effect.succeed(result.left)
80+ : Effect.die("Expected effect to fail"),
81+ ),
82+ );
83+
84+type OpenApiSpecServer = {
85+ baseUrl: string;
86+ specUrl: string;
87+ close: () => Promise<void>;
88+};
89+
90+const ownerParam = HttpApiSchema.param("owner", Schema.String);
91+const repoParam = HttpApiSchema.param("repo", Schema.String);
92+const issueIdParam = HttpApiSchema.param("issueId", Schema.String);
93+
94+class WorkspaceSourceReposApi extends HttpApiGroup.make("repos")
95+ .add(
96+ HttpApiEndpoint.get("getRepo")`/repos/${ownerParam}/${repoParam}`
97+ .addSuccess(Schema.Unknown),
98+ )
99+{}
100+
101+class WorkspaceSourceIssuesApi extends HttpApiGroup.make("issues")
102+ .add(
103+ HttpApiEndpoint.get("getIssue")`/issues/${issueIdParam}`
104+ .addSuccess(Schema.Unknown),
105+ )
106+ .add(
107+ HttpApiEndpoint.get("listIssues")`/issues`
108+ .addSuccess(Schema.Unknown),
109+ )
110+{}
111+
112+class WorkspaceSourceReposRootApi extends HttpApi.make("workspaceSourceRepos")
113+ .add(WorkspaceSourceReposApi)
114+{}
115+
116+class WorkspaceSourceIssuesRootApi extends HttpApi.make("workspaceSourceIssues")
117+ .add(WorkspaceSourceIssuesApi)
118+{}
119+
120+const workspaceSourceReposLive = HttpApiBuilder.group(
121+ WorkspaceSourceReposRootApi,
122+ "repos",
123+ (handlers) =>
124+ handlers.handle("getRepo", ({ path }) =>
125+ Effect.succeed({
126+ full_name: `${path.owner}/${path.repo}`,
127+ private: false,
128+ }),
129+ ),
130+);
131+
132+const workspaceSourceIssuesLive = HttpApiBuilder.group(
133+ WorkspaceSourceIssuesRootApi,
134+ "issues",
135+ (handlers) =>
136+ handlers
137+ .handle("getIssue", ({ path }) =>
138+ Effect.succeed({
139+ id: path.issueId,
140+ title: `Issue ${path.issueId}`,
141+ }),
142+ )
143+ .handle("listIssues", () =>
144+ Effect.succeed([
145+ {
146+ id: "1",
147+ title: "Issue 1",
148+ },
149+ ]),
150+ ),
151+);
152+
153+const workspaceSourceReposApiLayer = HttpApiBuilder.api(
154+ WorkspaceSourceReposRootApi,
155+).pipe(Layer.provide(workspaceSourceReposLive));
156+
157+const workspaceSourceIssuesApiLayer = HttpApiBuilder.api(
158+ WorkspaceSourceIssuesRootApi,
159+).pipe(Layer.provide(workspaceSourceIssuesLive));
160+
161+const createScopedExecutor = (input: {
162+ services: LocalExecutorServices;
163+ installation: LocalInstallation;
164+}) =>
165+ createExecutorEffect({
166+ backend: createExecutorBackend({
167+ loadServices: () =>
168+ Effect.succeed({
169+ ...input.services,
170+ scope: {
171+ ...input.services.scope,
172+ actorScopeId: input.installation.actorScopeId,
173+ resolutionScopeIds: input.installation.resolutionScopeIds,
174+ },
175+ storage: {
176+ ...input.services.storage,
177+ installation: {
178+ load: () => Effect.succeed(input.installation),
179+ getOrProvision: () => Effect.succeed(input.installation),
180+ },
181+ },
182+ }),
183+ }),
184+ getLocalServerBaseUrl: () => "http://127.0.0.1",
185+ });
186+
187+const makeSharedScopeExecutors = Effect.acquireRelease(
188+ Effect.gen(function* () {
189+ const fs = yield* FileSystem.FileSystem;
190+ const sharedScopeId = ScopeIdSchema.make("org_shared_sdk_pair_test");
191+
192+ const workspaceRootA = yield* fs.makeTempDirectoryScoped({
193+ prefix: "executor-sdk-scope-a-",
194+ });
195+ const workspaceRootB = yield* fs.makeTempDirectoryScoped({
196+ prefix: "executor-sdk-scope-b-",
197+ });
198+
199+ const servicesA = yield* createLocalExecutorServicesEffect({
200+ workspaceRoot: workspaceRootA,
201+ homeConfigPath: join(workspaceRootA, ".executor-home.jsonc"),
202+ homeStateDirectory: join(workspaceRootA, ".executor-home-state"),
203+ });
204+ const servicesB = yield* createLocalExecutorServicesEffect({
205+ workspaceRoot: workspaceRootB,
206+ homeConfigPath: join(workspaceRootB, ".executor-home.jsonc"),
207+ homeStateDirectory: join(workspaceRootB, ".executor-home-state"),
208+ });
209+
210+ const baseInstallationA =
211+ yield* servicesA.storage.installation.getOrProvision();
212+ const baseInstallationB =
213+ yield* servicesB.storage.installation.getOrProvision();
214+
215+ const sharedStorageClose = (() => {
216+ let closed = false;
217+ return async () => {
218+ if (closed) {
219+ return;
220+ }
221+ closed = true;
222+ await servicesA.storage.close?.();
223+ await servicesB.storage.close?.();
224+ };
225+ })();
226+
227+ const sharedStorage = {
228+ executorState: servicesA.storage.executorState,
229+ secretMaterial: servicesA.storage.secretMaterial,
230+ close: sharedStorageClose,
231+ };
232+
233+ const installationA: LocalInstallation = {
234+ ...baseInstallationA,
235+ actorScopeId: ScopeIdSchema.make("acc_sdk_executor_a"),
236+ resolutionScopeIds: [
237+ baseInstallationA.scopeId,
238+ sharedScopeId,
239+ ScopeIdSchema.make("acc_sdk_executor_a"),
240+ ],
241+ };
242+ const installationB: LocalInstallation = {
243+ ...baseInstallationB,
244+ actorScopeId: ScopeIdSchema.make("acc_sdk_executor_b"),
245+ resolutionScopeIds: [
246+ baseInstallationB.scopeId,
247+ sharedScopeId,
248+ ScopeIdSchema.make("acc_sdk_executor_b"),
249+ ],
250+ };
251+
252+ const executorA = yield* createScopedExecutor({
253+ services: {
254+ ...servicesA,
255+ storage: {
256+ ...servicesA.storage,
257+ ...sharedStorage,
258+ },
259+ },
260+ installation: installationA,
261+ });
262+ const executorB = yield* createScopedExecutor({
263+ services: {
264+ ...servicesB,
265+ storage: {
266+ ...servicesB.storage,
267+ ...sharedStorage,
268+ },
269+ },
270+ installation: installationB,
271+ });
272+
273+ return {
274+ executorA,
275+ executorB,
276+ sharedScopeId,
277+ };
278+ }).pipe(Effect.provide(NodeFileSystem.layer)),
279+ ({ executorA, executorB }) =>
280+ Effect.promise(async () => {
281+ await executorA.close();
282+ await executorB.close();
283+ }).pipe(Effect.orDie),
284+);
285+
286+describe("executor", () => {
287+ it.scoped(
288+ "keeps workspace sources isolated across executors that share an ancestor scope",
289+ () =>
290+ Effect.gen(function* () {
291+ const { executorA, executorB, sharedScopeId } =
292+ yield* makeSharedScopeExecutors;
293+ const openApiServer: OpenApiSpecServer = yield* makeOpenApiTestServer({
294+ apiLayer: workspaceSourceReposApiLayer,
295+ });
296+
297+ expect(executorA.resolutionScopeIds).toContain(sharedScopeId);
298+ expect(executorB.resolutionScopeIds).toContain(sharedScopeId);
299+
300+ const sourceA = yield* withExecutorApiClient(executorA, (client) =>
301+ client.sources.create({
302+ path: {
303+ workspaceId: executorA.scopeId,
304+ },
305+ payload: {
306+ name: "Workspace A Source",
307+ kind: "openapi",
308+ endpoint: openApiServer.baseUrl,
309+ namespace: "workspace.a",
310+ binding: {
311+ specUrl: openApiServer.specUrl,
312+ defaultHeaders: null,
313+ },
314+ auth: { kind: "none" },
315+ },
316+ }),
317+ );
318+ const sourceB = yield* withExecutorApiClient(executorB, (client) =>
319+ client.sources.create({
320+ path: {
321+ workspaceId: executorB.scopeId,
322+ },
323+ payload: {
324+ name: "Workspace B Source",
325+ kind: "openapi",
326+ endpoint: openApiServer.baseUrl,
327+ namespace: "workspace.b",
328+ binding: {
329+ specUrl: openApiServer.specUrl,
330+ defaultHeaders: null,
331+ },
332+ auth: { kind: "none" },
333+ },
334+ }),
335+ );
336+
337+ const listedA = yield* withExecutorApiClient(executorA, (client) =>
338+ client.sources.list({
339+ path: {
340+ workspaceId: executorA.scopeId,
341+ },
342+ }),
343+ );
344+ const listedB = yield* withExecutorApiClient(executorB, (client) =>
345+ client.sources.list({
346+ path: {
347+ workspaceId: executorB.scopeId,
348+ },
349+ }),
350+ );
351+
352+ expect(listedA.map((source) => source.id)).toEqual([sourceA.id]);
353+ expect(listedB.map((source) => source.id)).toEqual([sourceB.id]);
354+
355+ const lookupFromSibling = yield* expectLeft(
356+ withExecutorApiClient(executorB, (client) =>
357+ client.sources.get({
358+ path: {
359+ workspaceId: executorB.scopeId,
360+ sourceId: sourceA.id,
361+ },
362+ }),
363+ ),
364+ );
365+ assertInclude(String(lookupFromSibling), "Source not found");
366+ }),
367+ );
368+
369+ it.scoped(
370+ "keeps each executor's tool surface tied to its own workspace sources",
371+ () =>
372+ Effect.gen(function* () {
373+ const { executorA, executorB } =
374+ yield* makeSharedScopeExecutors;
375+ const openApiServerA: OpenApiSpecServer = yield* makeOpenApiTestServer({
376+ apiLayer: workspaceSourceReposApiLayer,
377+ });
378+ const openApiServerB: OpenApiSpecServer = yield* makeOpenApiTestServer({
379+ apiLayer: workspaceSourceIssuesApiLayer,
380+ });
381+
382+ const sourceA = yield* withExecutorApiClient(executorA, (client) =>
383+ client.sources.create({
384+ path: {
385+ workspaceId: executorA.scopeId,
386+ },
387+ payload: {
388+ name: "Workspace A Tools",
389+ kind: "openapi",
390+ endpoint: openApiServerA.baseUrl,
391+ namespace: "workspace.a",
392+ binding: {
393+ specUrl: openApiServerA.specUrl,
394+ defaultHeaders: null,
395+ },
396+ auth: { kind: "none" },
397+ },
398+ }),
399+ );
400+ const sourceB = yield* withExecutorApiClient(executorB, (client) =>
401+ client.sources.create({
402+ path: {
403+ workspaceId: executorB.scopeId,
404+ },
405+ payload: {
406+ name: "Workspace B Tools",
407+ kind: "openapi",
408+ endpoint: openApiServerB.baseUrl,
409+ namespace: "workspace.b",
410+ binding: {
411+ specUrl: openApiServerB.specUrl,
412+ defaultHeaders: null,
413+ },
414+ auth: { kind: "none" },
415+ },
416+ }),
417+ );
418+
419+ const inspectionA = yield* withExecutorApiClient(executorA, (client) =>
420+ client.sources.inspection({
421+ path: {
422+ workspaceId: executorA.scopeId,
423+ sourceId: sourceA.id,
424+ },
425+ }),
426+ );
427+ const inspectionB = yield* withExecutorApiClient(executorB, (client) =>
428+ client.sources.inspection({
429+ path: {
430+ workspaceId: executorB.scopeId,
431+ sourceId: sourceB.id,
432+ },
433+ }),
434+ );
435+
436+ expect(inspectionA.toolCount).toBe(1);
437+ expect(inspectionB.toolCount).toBe(2);
438+
439+ const siblingInspection = yield* expectLeft(
440+ withExecutorApiClient(executorA, (client) =>
441+ client.sources.inspection({
442+ path: {
443+ workspaceId: executorA.scopeId,
444+ sourceId: sourceB.id,
445+ },
446+ }),
447+ ),
448+ );
449+ assertInclude(String(siblingInspection), "Source not found");
450+ }),
451+ 60_000,
452+ );
453+});
packages/platform/sdk/src/executor.ts+95−78
@@ -1,19 +1,23 @@
11 import * as Effect from "effect/Effect";
22
33 import type {
4− AccountId,
4+ ScopeId,
55 Execution,
66 ExecutionEnvelope,
77 ExecutionInteraction,
88 LocalInstallation,
9− LocalWorkspacePolicy,
9+ LocalScopePolicy,
1010 ProviderAuthGrant,
1111 Source,
12− WorkspaceId,
13− WorkspaceOauthClient,
12+ ScopeOauthClient,
13+} from "./schema";
14+import {
15+ ExecutionIdSchema,
1416 } from "./schema";
15−import { ExecutionIdSchema } from "./schema";
16−import type { CreateExecutionPayload, ResumeExecutionPayload } from "./executions/contracts";
17+import type {
18+ CreateExecutionPayload,
19+ ResumeExecutionPayload,
20+} from "./executions/contracts";
1721 import type {
1822 CreateSecretPayload,
1923 CreateSecretResult,
@@ -36,7 +40,10 @@ import { · 16 unmodified lines
3640 listLocalSecrets,
3741 updateLocalSecret,
3842 } from "./local/secrets";
39−import type { CreatePolicyPayload, UpdatePolicyPayload } from "./policies/contracts";
43+import type {
44+ CreatePolicyPayload,
45+ UpdatePolicyPayload,
46+} from "./policies/contracts";
4047 import {
4148 createPolicy,
4249 getPolicy,
@@ -46,10 +53,12 @@ import { · 3 unmodified lines
4653 } from "./policies/operations";
4754 import type {
4855 CreateSourcePayload,
49− CreateWorkspaceOauthClientPayload,
56+ CreateScopeOauthClientPayload,
5057 UpdateSourcePayload,
5158 } from "./sources/contracts";
52−import { discoverSource } from "./sources/discovery";
59+import {
60+ discoverSource,
61+} from "./sources/discovery";
5362 import {
5463 discoverSourceInspectionTools,
5564 getSourceInspection,
@@ -62,17 +71,23 @@ import { · 6 unmodified lines
6271 removeSource,
6372 updateSource,
6473 } from "./sources/operations";
65−import type { ExecutorBackend } from "./backend";
74+import type {
75+ ExecutorBackend,
76+} from "./backend";
6677 import {
6778 provideExecutorRuntime,
6879 type ExecutorRuntime,
6980 type ExecutorRuntimeOptions,
70− type CreateWorkspaceInternalToolMap,
81+ type CreateScopeInternalToolMap,
7182 type ResolveExecutionEnvironment,
7283 type ResolveSecretMaterial,
7384 RuntimeSourceAuthServiceTag,
7485 } from "./runtime";
75−import { createExecution, getExecution, resumeExecution } from "./runtime/execution/service";
86+import {
87+ createExecution,
88+ getExecution,
89+ resumeExecution,
90+} from "./runtime/execution/service";
7691 import type {
7792 CompleteProviderOauthCallbackResult,
7893 CompleteSourceCredentialSetupResult,
@@ -96,29 +111,30 @@ type ProvidedEffect<T extends Effect.Effect<any, any, any>> = Effect.Effect< · 17 unmodified lines
96111
97112 export type ExecutorSourceInput = DistributiveOmit<
98113 ExecutorAddSourceInput,
99− "workspaceId" | "actorAccountId" | "executionId" | "interactionId"
114+ "scopeId" | "actorScopeId" | "executionId" | "interactionId"
100115 >;
101116
102117 export type ExecutorSourceBatchInput = DistributiveOmit<
103118 ConnectGoogleDiscoveryBatchInput,
104− "workspaceId" | "actorAccountId" | "executionId" | "interactionId"
119+ "scopeId" | "actorScopeId" | "executionId" | "interactionId"
105120 >;
106121
107122 export type ExecutorMcpSourceInput = DistributiveOmit<
108123 ConnectMcpSourceInput,
109− "workspaceId" | "actorAccountId"
124+ "scopeId" | "actorScopeId"
110125 >;
111126
112127 export type ExecutorSourceOAuthInput = DistributiveOmit<
113128 StartSourceOAuthSessionInput,
114− "workspaceId" | "actorAccountId"
129+ "scopeId" | "actorScopeId"
115130 >;
116131
117132 export type Executor = {
118133 runtime: ExecutorRuntime;
119134 installation: LocalInstallation;
120− workspaceId: WorkspaceId;
121− accountId: AccountId;
135+ scopeId: ScopeId;
136+ actorScopeId: ScopeId;
137+ resolutionScopeIds: ReadonlyArray<ScopeId>;
122138 provide: <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, any>;
123139 run: <A, E, R>(effect: Effect.Effect<A, E, R>) => Promise<A>;
124140 close: () => Promise<void>;
@@ -202,12 +218,12 @@ export type Executor = { · 77 unmodified lines
202218 oauthClients: {
203219 list: (
204220 providerKey: string,
205− ) => Effect.Effect<ReadonlyArray<WorkspaceOauthClient>, Error, never>;
221+ ) => Effect.Effect<ReadonlyArray<ScopeOauthClient>, Error, never>;
206222 create: (
207− payload: CreateWorkspaceOauthClientPayload,
208− ) => Effect.Effect<WorkspaceOauthClient, Error, never>;
223+ payload: CreateScopeOauthClientPayload,
224+ ) => Effect.Effect<ScopeOauthClient, Error, never>;
209225 remove: (
210− oauthClientId: WorkspaceOauthClient["id"],
226+ oauthClientId: ScopeOauthClient["id"],
211227 ) => Effect.Effect<boolean, Error, never>;
212228 };
213229 providerGrants: {
@@ -225,8 +241,8 @@ export type Executor = { · 11 unmodified lines
225241 errorDescription?: string | null;
226242 }) => Effect.Effect<CompleteSourceOAuthSessionResult, Error, never>;
227243 completeProviderCallback: (input: {
228− workspaceId?: WorkspaceId;
229− actorAccountId?: AccountId | null;
244+ scopeId?: ScopeId;
245+ actorScopeId?: ScopeId | null;
230246 state: string;
231247 code?: string | null;
232248 error?: string | null;
@@ -275,13 +291,13 @@ export type Executor = { · 42 unmodified lines
275291 remove: (secretId: string) => Promise<DeleteSecretResult>;
276292 };
277293 policies: {
278− list: () => Promise<ReadonlyArray<LocalWorkspacePolicy>>;
279− create: (payload: CreatePolicyPayload) => Promise<LocalWorkspacePolicy>;
280− get: (policyId: string) => Promise<LocalWorkspacePolicy>;
294+ list: () => Promise<ReadonlyArray<LocalScopePolicy>>;
295+ create: (payload: CreatePolicyPayload) => Promise<LocalScopePolicy>;
296+ get: (policyId: string) => Promise<LocalScopePolicy>;
281297 update: (
282298 policyId: string,
283299 payload: UpdatePolicyPayload,
284− ) => Promise<LocalWorkspacePolicy>;
300+ ) => Promise<LocalScopePolicy>;
285301 remove: (policyId: string) => Promise<boolean>;
286302 };
287303 sources: {
@@ -314,11 +330,11 @@ export type Executor = { · 26 unmodified lines
314330 }) => Promise<Effect.Effect.Success<ReturnType<typeof discoverSourceInspectionTools>>>;
315331 };
316332 oauthClients: {
317− list: (providerKey: string) => Promise<ReadonlyArray<WorkspaceOauthClient>>;
333+ list: (providerKey: string) => Promise<ReadonlyArray<ScopeOauthClient>>;
318334 create: (
319− payload: CreateWorkspaceOauthClientPayload,
320− ) => Promise<WorkspaceOauthClient>;
321− remove: (oauthClientId: WorkspaceOauthClient["id"]) => Promise<boolean>;
335+ payload: CreateScopeOauthClientPayload,
336+ ) => Promise<ScopeOauthClient>;
337+ remove: (oauthClientId: ScopeOauthClient["id"]) => Promise<boolean>;
322338 };
323339 providerGrants: {
324340 remove: (grantId: ProviderAuthGrant["id"]) => Promise<boolean>;
@@ -333,8 +349,8 @@ export type Executor = { · 8 unmodified lines
333349 errorDescription?: string | null;
334350 }) => Promise<CompleteSourceOAuthSessionResult>;
335351 completeProviderCallback: (input: {
336− workspaceId?: WorkspaceId;
337− actorAccountId?: AccountId | null;
352+ scopeId?: ScopeId;
353+ actorScopeId?: ScopeId | null;
338354 state: string;
339355 code?: string | null;
340356 error?: string | null;
@@ -357,8 +373,8 @@ export type CreateExecutorOptions = ExecutorRuntimeOptions & { · 16 unmodified lines
357373
358374 const fromRuntime = (runtime: ExecutorRuntime): Executor => {
359375 const installation = runtime.localInstallation;
360− const workspaceId = installation.workspaceId;
361− const accountId = installation.accountId;
376+ const scopeId = installation.scopeId;
377+ const actorScopeId = installation.actorScopeId;
362378 const provide = <A, E, R>(effect: Effect.Effect<A, E, R>) =>
363379 provideExecutorRuntime(effect, runtime);
364380 const run = <A, E, R>(effect: Effect.Effect<A, E, R>) =>
@@ -383,7 +399,7 @@ const fromRuntime = (runtime: ExecutorRuntime): Executor => { · 18 unmodified lines
383399 get: ({ sourceId, interactionId }) =>
384400 provide(
385401 getSourceCredentialInteraction({
386− workspaceId,
402+ scopeId,
387403 sourceId,
388404 interactionId,
389405 }),
@@ -391,7 +407,7 @@ const fromRuntime = (runtime: ExecutorRuntime): Executor => { · 1 unmodified line
391407 submit: ({ sourceId, interactionId, action, token }) =>
392408 provide(
393409 submitSourceCredentialInteraction({
394− workspaceId,
410+ scopeId,
395411 sourceId,
396412 interactionId,
397413 action,
@@ -401,7 +417,7 @@ const fromRuntime = (runtime: ExecutorRuntime): Executor => { · 3 unmodified lines
401417 complete: ({ sourceId, state, code, error, errorDescription }) =>
402418 provide(
403419 completeSourceCredentialSetup({
404− workspaceId,
420+ scopeId,
405421 sourceId,
406422 state,
407423 code,
@@ -419,15 +435,15 @@ const fromRuntime = (runtime: ExecutorRuntime): Executor => { · 11 unmodified lines
419435 remove: (secretId: string) => provide(deleteLocalSecret(secretId)),
420436 },
421437 policies: {
422− list: () => provide(listPolicies(workspaceId)),
438+ list: () => provide(listPolicies(scopeId)),
423439 create: (payload: CreatePolicyPayload) =>
424− provide(createPolicy({ workspaceId, payload })),
440+ provide(createPolicy({ scopeId, payload })),
425441 get: (policyId: string) =>
426− provide(getPolicy({ workspaceId, policyId: policyId as never })),
442+ provide(getPolicy({ scopeId, policyId: policyId as never })),
427443 update: (policyId: string, payload: UpdatePolicyPayload) =>
428− provide(updatePolicy({ workspaceId, policyId: policyId as never, payload })),
444+ provide(updatePolicy({ scopeId, policyId: policyId as never, payload })),
429445 remove: (policyId: string) =>
430− provide(removePolicy({ workspaceId, policyId: policyId as never })),
446+ provide(removePolicy({ scopeId, policyId: policyId as never })),
431447 },
432448 sources: {
433449 add: (input: ExecutorSourceInput, options?: { baseUrl?: string | null }) =>
@@ -436,8 +452,8 @@ const fromRuntime = (runtime: ExecutorRuntime): Executor => { · 2 unmodified lines
436452 return service.addExecutorSource(
437453 {
438454 ...input,
439− workspaceId,
440− actorAccountId: accountId,
455+ scopeId,
456+ actorScopeId: actorScopeId,
441457 executionId: session.executionId,
442458 interactionId: session.interactionId,
443459 },
@@ -448,8 +464,8 @@ const fromRuntime = (runtime: ExecutorRuntime): Executor => { · 4 unmodified lines
448464 provideSourceAuth((service) =>
449465 service.connectMcpSource({
450466 ...payload,
451− workspaceId,
452− actorAccountId: accountId,
467+ scopeId,
468+ actorScopeId: actorScopeId,
453469 }),
454470 ),
455471 connectBatch: (payload: ExecutorSourceBatchInput) =>
@@ -457,8 +473,8 @@ const fromRuntime = (runtime: ExecutorRuntime): Executor => { · 1 unmodified line
457473 const session = createSdkSourceSession();
458474 return service.connectGoogleDiscoveryBatch({
459475 ...payload,
460− workspaceId,
461− actorAccountId: accountId,
476+ scopeId,
477+ actorScopeId: actorScopeId,
462478 executionId: session.executionId,
463479 interactionId: session.interactionId,
464480 });
@@ -467,22 +483,22 @@ const fromRuntime = (runtime: ExecutorRuntime): Executor => { · 2 unmodified lines
467483 url: string;
468484 probeAuth?: Parameters<typeof discoverSource>[0]["probeAuth"];
469485 }) => provide(discoverSource(input)),
470− list: () => provide(listSources({ workspaceId, accountId })),
486+ list: () => provide(listSources({ scopeId, actorScopeId })),
471487 create: (payload: CreateSourcePayload) =>
472− provide(createSource({ workspaceId, accountId, payload })),
488+ provide(createSource({ scopeId, actorScopeId, payload })),
473489 get: (sourceId: Source["id"]) =>
474− provide(getSource({ workspaceId, sourceId, accountId })),
490+ provide(getSource({ scopeId, sourceId, actorScopeId })),
475491 update: (sourceId: Source["id"], payload: UpdateSourcePayload) =>
476− provide(updateSource({ workspaceId, sourceId, accountId, payload })),
492+ provide(updateSource({ scopeId, sourceId, actorScopeId, payload })),
477493 remove: (sourceId: Source["id"]) =>
478− provide(removeSource({ workspaceId, sourceId })),
494+ provide(removeSource({ scopeId, sourceId })),
479495 inspection: {
480496 get: (sourceId: Source["id"]) =>
481− provide(getSourceInspection({ workspaceId, sourceId })),
497+ provide(getSourceInspection({ scopeId, sourceId })),
482498 tool: ({ sourceId, toolPath }: { sourceId: Source["id"]; toolPath: string }) =>
483499 provide(
484500 getSourceInspectionToolDetail({
485− workspaceId,
501+ scopeId,
486502 sourceId,
487503 toolPath,
488504 }),
@@ -496,7 +512,7 @@ const fromRuntime = (runtime: ExecutorRuntime): Executor => { · 7 unmodified lines
496512 }) =>
497513 provide(
498514 discoverSourceInspectionTools({
499− workspaceId,
515+ scopeId,
500516 sourceId,
501517 payload,
502518 }),
@@ -505,24 +521,24 @@ const fromRuntime = (runtime: ExecutorRuntime): Executor => { · 2 unmodified lines
505521 oauthClients: {
506522 list: (providerKey: string) =>
507523 provideSourceAuth((service) =>
508− service.listWorkspaceOauthClients({
509− workspaceId,
524+ service.listScopeOauthClients({
525+ scopeId,
510526 providerKey,
511527 }),
512528 ),
513− create: (payload: CreateWorkspaceOauthClientPayload) =>
529+ create: (payload: CreateScopeOauthClientPayload) =>
514530 provideSourceAuth((service) =>
515− service.createWorkspaceOauthClient({
516− workspaceId,
531+ service.createScopeOauthClient({
532+ scopeId,
517533 providerKey: payload.providerKey,
518534 label: payload.label,
519535 oauthClient: payload.oauthClient,
520536 }),
521537 ),
522− remove: (oauthClientId: WorkspaceOauthClient["id"]) =>
538+ remove: (oauthClientId: ScopeOauthClient["id"]) =>
523539 provideSourceAuth((service) =>
524− service.removeWorkspaceOauthClient({
525− workspaceId,
540+ service.removeScopeOauthClient({
541+ scopeId,
526542 oauthClientId,
527543 }),
528544 ),
@@ -531,7 +547,7 @@ const fromRuntime = (runtime: ExecutorRuntime): Executor => { · 2 unmodified lines
531547 remove: (grantId: ProviderAuthGrant["id"]) =>
532548 provideSourceAuth((service) =>
533549 service.removeProviderAuthGrant({
534− workspaceId,
550+ scopeId,
535551 grantId,
536552 }),
537553 ),
@@ -542,8 +558,8 @@ const fromRuntime = (runtime: ExecutorRuntime): Executor => { · 4 unmodified lines
542558 provideSourceAuth((service) =>
543559 service.startSourceOAuthSession({
544560 ...input,
545− workspaceId,
546− actorAccountId: accountId,
561+ scopeId,
562+ actorScopeId: actorScopeId,
547563 }),
548564 ),
549565 completeSourceAuth: ({ state, code, error, errorDescription }) =>
@@ -559,8 +575,8 @@ const fromRuntime = (runtime: ExecutorRuntime): Executor => { · 9 unmodified lines
559575 provideSourceAuth((service) =>
560576 service.completeProviderOauthCallback({
561577 ...input,
562− workspaceId: input.workspaceId ?? workspaceId,
563− actorAccountId: input.actorAccountId ?? accountId,
578+ scopeId: input.scopeId ?? scopeId,
579+ actorScopeId: input.actorScopeId ?? actorScopeId,
564580 }),
565581 ),
566582 },
@@ -568,20 +584,20 @@ const fromRuntime = (runtime: ExecutorRuntime): Executor => { · 1 unmodified line
568584 create: (payload: CreateExecutionPayload) =>
569585 provide(
570586 createExecution({
571− workspaceId,
587+ scopeId,
572588 payload,
573− createdByAccountId: accountId,
589+ createdByScopeId: actorScopeId,
574590 }),
575591 ),
576592 get: (executionId: Execution["id"]) =>
577− provide(getExecution({ workspaceId, executionId })),
593+ provide(getExecution({ scopeId, executionId })),
578594 resume: (executionId: Execution["id"], payload: ResumeExecutionPayload) =>
579595 provide(
580596 resumeExecution({
581− workspaceId,
597+ scopeId,
582598 executionId,
583599 payload,
584− resumedByAccountId: accountId,
600+ resumedByScopeId: actorScopeId,
585601 }),
586602 ),
587603 },
@@ -590,8 +606,9 @@ const fromRuntime = (runtime: ExecutorRuntime): Executor => { · 2 unmodified lines
590606 return {
591607 runtime,
592608 installation,
593− workspaceId,
594− accountId,
609+ scopeId,
610+ actorScopeId,
611+ resolutionScopeIds: installation.resolutionScopeIds,
595612 provide,
596613 run,
597614 close: () => runtime.close(),
packages/platform/sdk/src/local/contracts.ts+3−1
@@ -1,4 +1,6 @@
1−import { SecretMaterialPurposeSchema } from "../schema";
1+import {
2+ SecretMaterialPurposeSchema,
3+} from "../schema";
24 import * as Schema from "effect/Schema";
35
46 export const SecretProviderSchema = Schema.Struct({
packages/platform/sdk/src/local/operations.ts+32−20
@@ -2,25 +2,37 @@ import type { · 1 unmodified line
22 Execution,
33 ExecutionInteraction,
44 Source,
5− WorkspaceId,
5+ ScopeId,
66 } from "../schema";
77 import {
88 SourceIdSchema,
9− WorkspaceIdSchema,
9+ ScopeIdSchema,
1010 } from "../schema";
1111 import * as Effect from "effect/Effect";
1212 import * as Option from "effect/Option";
1313
14−import { submitExecutionInteractionResponse } from "../runtime/execution/service";
15−import { LiveExecutionManagerService } from "../runtime/execution/live";
16−import { operationErrors } from "../runtime/policy/operation-errors";
17−import { InstallationStore } from "../runtime/workspace/storage";
18−import { RuntimeSourceAuthServiceTag } from "../runtime/sources/source-auth-service";
14+import {
15+ submitExecutionInteractionResponse,
16+} from "../runtime/execution/service";
17+import {
18+ LiveExecutionManagerService,
19+} from "../runtime/execution/live";
20+import {
21+ operationErrors,
22+} from "../runtime/policy/operation-errors";
23+import {
24+ InstallationStore,
25+} from "../runtime/scope/storage";
26+import {
27+ RuntimeSourceAuthServiceTag,
28+} from "../runtime/sources/source-auth-service";
1929 import {
2030 createSourceCredentialSelectionBearerContent,
2131 createSourceCredentialSelectionNoneContent,
2232 } from "../runtime/sources/source-credential-interactions";
23−import { ExecutorStateStore } from "../runtime/executor-state-store";
33+import {
34+ ExecutorStateStore,
35+} from "../runtime/executor-state-store";
2436
2537 const localOps = {
2638 installation: operationErrors("local.installation.get"),
@@ -34,7 +46,7 @@ type SourceCredentialInteraction = { · 7 unmodified lines
3446 executionId: Execution["id"];
3547 status: ExecutionInteraction["status"];
3648 message: string;
37− workspaceId: WorkspaceId;
49+ scopeId: ScopeId;
3850 sourceId: Source["id"];
3951 sourceLabel: string;
4052 endpoint: string;
@@ -100,10 +112,10 @@ export const decodeSourceCredentialInteraction = ( · 59 unmodified lines
100112 return null;
101113 }
102114
103− const workspaceId = trimOrNull(asString(args.workspaceId));
115+ const scopeId = trimOrNull(asString(args.scopeId));
104116 const sourceId = trimOrNull(asString(args.sourceId));
105117 const message = trimOrNull(asString(elicitation.message));
106− if (workspaceId === null || sourceId === null || message === null) {
118+ if (scopeId === null || sourceId === null || message === null) {
107119 return null;
108120 }
109121
@@ -112,7 +124,7 @@ export const decodeSourceCredentialInteraction = ( · 2 unmodified lines
112124 executionId: interaction.executionId,
113125 status: interaction.status,
114126 message,
115− workspaceId: WorkspaceIdSchema.make(workspaceId),
127+ scopeId: ScopeIdSchema.make(scopeId),
116128 sourceId: SourceIdSchema.make(sourceId),
117129 };
118130 } catch {
@@ -121,7 +133,7 @@ export const decodeSourceCredentialInteraction = ( · 2 unmodified lines
121133 };
122134
123135 const loadSourceCredentialInteraction = (input: {
124− workspaceId: WorkspaceId;
136+ scopeId: ScopeId;
125137 sourceId: Source["id"];
126138 interactionId: ExecutionInteraction["id"];
127139 operation:
@@ -153,18 +165,18 @@ const loadSourceCredentialInteraction = (input: { · 25 unmodified lines
153165 const decoded = decodeSourceCredentialInteraction(stored.value);
154166 if (
155167 decoded === null ||
156− decoded.workspaceId !== input.workspaceId ||
168+ decoded.scopeId !== input.scopeId ||
157169 decoded.sourceId !== input.sourceId
158170 ) {
159171 return yield* input.operation.notFound(
160172 "Source credential request not found",
161− `workspaceId=${input.workspaceId} sourceId=${input.sourceId} interactionId=${input.interactionId}`,
173+ `scopeId=${input.scopeId} sourceId=${input.sourceId} interactionId=${input.interactionId}`,
162174 );
163175 }
164176
165177 const source = yield* sourceAuthService
166178 .getSourceById({
167− workspaceId: input.workspaceId,
179+ scopeId: input.scopeId,
168180 sourceId: input.sourceId,
169181 })
170182 .pipe(
@@ -197,7 +209,7 @@ export const getLocalInstallation = () => · 26 unmodified lines
197209 });
198210
199211 export const getSourceCredentialInteraction = (input: {
200− workspaceId: WorkspaceId;
212+ scopeId: ScopeId;
201213 sourceId: Source["id"];
202214 interactionId: ExecutionInteraction["id"];
203215 }) =>
@@ -207,7 +219,7 @@ export const getSourceCredentialInteraction = (input: { · 3 unmodified lines
207219 });
208220
209221 export const submitSourceCredentialInteraction = (input: {
210− workspaceId: WorkspaceId;
222+ scopeId: ScopeId;
211223 sourceId: Source["id"];
212224 interactionId: ExecutionInteraction["id"];
213225 action: "submit" | "continue" | "cancel";
@@ -215,7 +227,7 @@ export const submitSourceCredentialInteraction = (input: { · 1 unmodified line
215227 }) =>
216228 Effect.gen(function* () {
217229 const interaction = yield* loadSourceCredentialInteraction({
218− workspaceId: input.workspaceId,
230+ scopeId: input.scopeId,
219231 sourceId: input.sourceId,
220232 interactionId: input.interactionId,
221233 operation: localOps.sourceCredentialSubmit,
@@ -370,7 +382,7 @@ export const submitSourceCredentialInteraction = (input: { · 148 unmodified lines
370382 });
371383
372384 export const completeSourceCredentialSetup = (input: {
373− workspaceId: WorkspaceId;
385+ scopeId: ScopeId;
374386 sourceId: Source["id"];
375387 state: string;
376388 code?: string | null;
packages/platform/sdk/src/local/secrets.ts+18−10
@@ -1,7 +1,9 @@
11 import * as Effect from "effect/Effect";
22 import * as Option from "effect/Option";
33
4−import { SecretMaterialIdSchema } from "../schema";
4+import {
5+ SecretMaterialIdSchema,
6+} from "../schema";
57 import {
68 type CreateSecretPayload,
79 type CreateSecretResult,
@@ -16,15 +18,21 @@ import { · 8 unmodified lines
1618 ControlPlaneNotFoundError,
1719 ControlPlaneStorageError,
1820 } from "../errors";
19−import { requireRuntimeLocalWorkspace } from "../runtime/workspace/runtime-context";
21+import {
22+ requireRuntimeLocalScope,
23+} from "../runtime/scope/runtime-context";
2024 import {
2125 LocalInstanceConfigService,
2226 SecretMaterialDeleterService,
2327 SecretMaterialStorerService,
2428 SecretMaterialUpdaterService,
25−} from "../runtime/workspace/secret-material-providers";
26−import { RuntimeSourceStoreService } from "../runtime/sources/source-store";
27−import { ExecutorStateStore } from "../runtime/executor-state-store";
29+} from "../runtime/scope/secret-material-providers";
30+import {
31+ RuntimeSourceStoreService,
32+} from "../runtime/sources/source-store";
33+import {
34+ ExecutorStateStore,
35+} from "../runtime/executor-state-store";
2836
2937 const secretStorageError = (operation: string, message: string) =>
3038 new ControlPlaneStorageError({
@@ -40,9 +48,9 @@ export const listLocalSecrets = () => · 9 unmodified lines
4048 Effect.gen(function* () {
4149 const store = yield* ExecutorStateStore;
4250 const sourceStore = yield* RuntimeSourceStoreService;
43− const runtimeLocalWorkspace = yield* requireRuntimeLocalWorkspace().pipe(
51+ const runtimeLocalScope = yield* requireRuntimeLocalScope().pipe(
4452 Effect.mapError(() =>
45− secretStorageError("secrets.list", "Failed resolving local workspace."),
53+ secretStorageError("secrets.list", "Failed resolving local scope."),
4654 ),
4755 );
4856 const secretMaterials = yield* store.secretMaterials
@@ -53,10 +61,10 @@ export const listLocalSecrets = () => · 4 unmodified lines
5361 ),
5462 );
5563 const linkedSourcesMap = yield* sourceStore
56− .listLinkedSecretSourcesInWorkspace(
57− runtimeLocalWorkspace.installation.workspaceId,
64+ .listLinkedSecretSourcesInScope(
65+ runtimeLocalScope.installation.scopeId,
5866 {
59− actorAccountId: runtimeLocalWorkspace.installation.accountId,
67+ actorScopeId: runtimeLocalScope.installation.actorScopeId,
6068 },
6169 )
6270 .pipe(
packages/platform/sdk/src/policies/contracts.ts+10−8
@@ -1,23 +1,25 @@
11 import {
2− LocalWorkspacePolicyApprovalModeSchema,
3− LocalWorkspacePolicyEffectSchema,
2+ LocalScopePolicyApprovalModeSchema,
3+ LocalScopePolicyEffectSchema,
44 } from "../schema";
55 import * as Schema from "effect/Schema";
66
7−import { OptionalTrimmedNonEmptyStringSchema } from "../string-schemas";
7+import {
8+ OptionalTrimmedNonEmptyStringSchema,
9+} from "../string-schemas";
810
9−const LocalWorkspacePolicyPayloadSchema = Schema.Struct({
11+const LocalScopePolicyPayloadSchema = Schema.Struct({
1012 resourcePattern: OptionalTrimmedNonEmptyStringSchema,
11− effect: Schema.optional(LocalWorkspacePolicyEffectSchema),
12− approvalMode: Schema.optional(LocalWorkspacePolicyApprovalModeSchema),
13+ effect: Schema.optional(LocalScopePolicyEffectSchema),
14+ approvalMode: Schema.optional(LocalScopePolicyApprovalModeSchema),
1315 priority: Schema.optional(Schema.Number),
1416 enabled: Schema.optional(Schema.Boolean),
1517 });
1618
17−export const CreatePolicyPayloadSchema = LocalWorkspacePolicyPayloadSchema;
19+export const CreatePolicyPayloadSchema = LocalScopePolicyPayloadSchema;
1820
1921 export type CreatePolicyPayload = typeof CreatePolicyPayloadSchema.Type;
2022
21−export const UpdatePolicyPayloadSchema = LocalWorkspacePolicyPayloadSchema;
23+export const UpdatePolicyPayloadSchema = LocalScopePolicyPayloadSchema;
2224
2325 export type UpdatePolicyPayload = typeof UpdatePolicyPayloadSchema.Type;
packages/platform/sdk/src/policies/operations.ts+135−127
@@ -1,4 +1,6 @@
1−import { createHash } from "node:crypto";
1+import {
2+ createHash,
3+} from "node:crypto";
24
35 import type {
46 CreatePolicyPayload,
@@ -7,23 +9,29 @@ import type { · 2 unmodified lines
79 import {
810 PolicyIdSchema,
911 type LocalExecutorConfig,
10− type LocalWorkspacePolicy,
12+ type LocalScopePolicy,
1113 type PolicyId,
12− type WorkspaceId,
14+ type ScopeId,
1315 } from "../schema";
1416 import * as Effect from "effect/Effect";
1517
16−import { requireRuntimeLocalWorkspace } from "../runtime/workspace/runtime-context";
18+import {
19+ requireRuntimeLocalScope,
20+} from "../runtime/scope/runtime-context";
1721 import type {
18− WorkspaceConfigStoreShape,
19− WorkspaceStateStoreShape,
20−} from "../runtime/workspace/storage";
22+ ScopeConfigStoreShape,
23+ ScopeStateStoreShape,
24+} from "../runtime/scope/storage";
25+import {
26+ ScopeConfigStore,
27+ ScopeStateStore,
28+} from "../runtime/scope/storage";
29+import {
30+ type LocalScopeState,
31+} from "../runtime/scope-state";
2132 import {
22− WorkspaceConfigStore,
23− WorkspaceStateStore,
24−} from "../runtime/workspace/storage";
25−import { type LocalWorkspaceState } from "../runtime/workspace-state";
26−import { derivePolicyConfigKey } from "../runtime/workspace/workspace-sync";
33+ derivePolicyConfigKey,
34+} from "../runtime/scope/scope-sync";
2735 import {
2836 type OperationErrors,
2937 operationErrors,
@@ -39,34 +47,34 @@ const policyOps = { · 9 unmodified lines
3947
4048 const cloneJson = <T>(value: T): T => JSON.parse(JSON.stringify(value)) as T;
4149
42−const workspacePolicyStableKey = (input: {
43− workspaceId: WorkspaceId;
44− workspaceRoot?: string | null;
45−}): string => input.workspaceRoot?.trim() || input.workspaceId;
50+const scopePolicyStableKey = (input: {
51+ scopeId: ScopeId;
52+ scopeRoot?: string | null;
53+}): string => input.scopeRoot?.trim() || input.scopeId;
4654
4755 const localPolicyIdForKey = (input: {
48− workspaceStableKey: string;
56+ scopeStableKey: string;
4957 key: string;
5058 }): PolicyId =>
5159 PolicyIdSchema.make(
52− `pol_local_${createHash("sha256").update(`${input.workspaceStableKey}:${input.key}`).digest("hex").slice(0, 16)}`,
60+ `pol_local_${createHash("sha256").update(`${input.scopeStableKey}:${input.key}`).digest("hex").slice(0, 16)}`,
5361 );
5462
55−const toLocalWorkspacePolicy = (input: {
56− workspaceId: WorkspaceId;
57− workspaceStableKey: string;
63+const toLocalScopePolicy = (input: {
64+ scopeId: ScopeId;
65+ scopeStableKey: string;
5866 key: string;
5967 policyConfig: NonNullable<LocalExecutorConfig["policies"]>[string];
60− state: LocalWorkspaceState["policies"][string] | undefined;
61−}): LocalWorkspacePolicy => ({
68+ state: LocalScopeState["policies"][string] | undefined;
69+}): LocalScopePolicy => ({
6270 id:
6371 input.state?.id ??
6472 localPolicyIdForKey({
65− workspaceStableKey: input.workspaceStableKey,
73+ scopeStableKey: input.scopeStableKey,
6674 key: input.key,
6775 }),
6876 key: input.key,
69− workspaceId: input.workspaceId,
77+ scopeId: input.scopeId,
7078 resourcePattern: input.policyConfig.match.trim(),
7179 effect: input.policyConfig.action,
7280 approvalMode: input.policyConfig.approval === "manual" ? "required" : "auto",
@@ -76,51 +84,51 @@ const toLocalWorkspacePolicy = (input: { · 3 unmodified lines
7684 updatedAt: input.state?.updatedAt ?? Date.now(),
7785 });
7886
79−export const loadRuntimeLocalWorkspacePolicies = (workspaceId: WorkspaceId) =>
87+export const loadRuntimeLocalScopePolicies = (scopeId: ScopeId) =>
8088 Effect.gen(function* () {
81− const runtimeLocalWorkspace =
82− yield* requireRuntimeLocalWorkspace(workspaceId);
83− const workspaceConfigStore = yield* WorkspaceConfigStore;
84− const workspaceStateStore = yield* WorkspaceStateStore;
85− const loadedConfig = yield* workspaceConfigStore.load();
86− const workspaceState = yield* workspaceStateStore.load();
89+ const runtimeLocalScope =
90+ yield* requireRuntimeLocalScope(scopeId);
91+ const scopeConfigStore = yield* ScopeConfigStore;
92+ const scopeStateStore = yield* ScopeStateStore;
93+ const loadedConfig = yield* scopeConfigStore.load();
94+ const scopeState = yield* scopeStateStore.load();
8795
8896 const policies = Object.entries(loadedConfig.config?.policies ?? {}).map(
8997 ([key, policyConfig]) =>
90− toLocalWorkspacePolicy({
91− workspaceId,
92− workspaceStableKey: workspacePolicyStableKey({
93− workspaceId,
94− workspaceRoot: runtimeLocalWorkspace.workspace.workspaceRoot,
98+ toLocalScopePolicy({
99+ scopeId,
100+ scopeStableKey: scopePolicyStableKey({
101+ scopeId,
102+ scopeRoot: runtimeLocalScope.scope.scopeRoot,
95103 }),
96104 key,
97105 policyConfig,
98− state: workspaceState.policies[key],
106+ state: scopeState.policies[key],
99107 }),
100108 );
101109
102110 return {
103− runtimeLocalWorkspace,
111+ runtimeLocalScope,
104112 loadedConfig,
105− workspaceState,
113+ scopeState,
106114 policies,
107115 };
108116 });
109117
110118 const writeLocalPolicyFiles = (input: {
111119 operation: OperationErrors;
112− workspaceConfigStore: WorkspaceConfigStoreShape;
113− workspaceStateStore: WorkspaceStateStoreShape;
120+ scopeConfigStore: ScopeConfigStoreShape;
121+ scopeStateStore: ScopeStateStoreShape;
114122 projectConfig: LocalExecutorConfig;
115− workspaceState: LocalWorkspaceState;
123+ scopeState: LocalScopeState;
116124 }) =>
117125 Effect.all(
118126 [
119− input.workspaceConfigStore.writeProject({
127+ input.scopeConfigStore.writeProject({
120128 config: input.projectConfig,
121129 }),
122− input.workspaceStateStore.write({
123− state: input.workspaceState,
130+ input.scopeStateStore.write({
131+ state: input.scopeState,
124132 }),
125133 ],
126134 { discard: true },
@@ -128,16 +136,16 @@ const writeLocalPolicyFiles = (input: { · 1 unmodified line
128136 Effect.mapError((cause) =>
129137 input.operation.unknownStorage(
130138 cause,
131− "Failed writing local workspace policy files",
139+ "Failed writing local scope policy files",
132140 ),
133141 ),
134142 );
135143
136−const loadWorkspacePolicyContext = (
144+const loadScopePolicyContext = (
137145 operation: OperationErrors,
138− workspaceId: WorkspaceId,
146+ scopeId: ScopeId,
139147 ) =>
140− requireRuntimeLocalWorkspace(workspaceId).pipe(
148+ requireRuntimeLocalScope(scopeId).pipe(
141149 Effect.mapError((cause) =>
142150 operation.notFound(
143151 "Workspace not found",
@@ -146,46 +154,46 @@ const loadWorkspacePolicyContext = ( · 2 unmodified lines
146154 ),
147155 );
148156
149−export const listPolicies = (workspaceId: WorkspaceId) =>
157+export const listPolicies = (scopeId: ScopeId) =>
150158 Effect.gen(function* () {
151− yield* loadWorkspacePolicyContext(policyOps.list, workspaceId);
152− const localWorkspace = yield* loadRuntimeLocalWorkspacePolicies(
153− workspaceId,
159+ yield* loadScopePolicyContext(policyOps.list, scopeId);
160+ const localScope = yield* loadRuntimeLocalScopePolicies(
161+ scopeId,
154162 ).pipe(
155163 Effect.mapError((cause) =>
156164 policyOps.list.unknownStorage(
157165 cause,
158− "Failed loading local workspace policies",
166+ "Failed loading local scope policies",
159167 ),
160168 ),
161169 );
162− return localWorkspace.policies;
170+ return localScope.policies;
163171 });
164172
165173 export const createPolicy = (input: {
166− workspaceId: WorkspaceId;
174+ scopeId: ScopeId;
167175 payload: CreatePolicyPayload;
168176 }) =>
169177 Effect.gen(function* () {
170− const runtimeLocalWorkspace = yield* loadWorkspacePolicyContext(
178+ const runtimeLocalScope = yield* loadScopePolicyContext(
171179 policyOps.create,
172− input.workspaceId,
180+ input.scopeId,
173181 );
174− const workspaceConfigStore = yield* WorkspaceConfigStore;
175− const workspaceStateStore = yield* WorkspaceStateStore;
176− const localWorkspace = yield* loadRuntimeLocalWorkspacePolicies(
177− input.workspaceId,
182+ const scopeConfigStore = yield* ScopeConfigStore;
183+ const scopeStateStore = yield* ScopeStateStore;
184+ const localScope = yield* loadRuntimeLocalScopePolicies(
185+ input.scopeId,
178186 ).pipe(
179187 Effect.mapError((cause) =>
180188 policyOps.create.unknownStorage(
181189 cause,
182− "Failed loading local workspace policies",
190+ "Failed loading local scope policies",
183191 ),
184192 ),
185193 );
186194 const now = Date.now();
187195 const projectConfig = cloneJson(
188− localWorkspace.loadedConfig.projectConfig ?? {},
196+ localScope.loadedConfig.projectConfig ?? {},
189197 );
190198 const policies = { ...projectConfig.policies };
191199 const key = derivePolicyConfigKey(
@@ -210,18 +218,18 @@ export const createPolicy = (input: { · 18 unmodified lines
210218 : {}),
211219 };
212220
213− const existingState = localWorkspace.workspaceState.policies[key];
214− const workspaceState: LocalWorkspaceState = {
215− ...localWorkspace.workspaceState,
221+ const existingState = localScope.scopeState.policies[key];
222+ const scopeState: LocalScopeState = {
223+ ...localScope.scopeState,
216224 policies: {
217− ...localWorkspace.workspaceState.policies,
225+ ...localScope.scopeState.policies,
218226 [key]: {
219227 id:
220228 existingState?.id ??
221229 localPolicyIdForKey({
222− workspaceStableKey: workspacePolicyStableKey({
223− workspaceId: input.workspaceId,
224− workspaceRoot: runtimeLocalWorkspace.workspace.workspaceRoot,
230+ scopeStableKey: scopePolicyStableKey({
231+ scopeId: input.scopeId,
232+ scopeRoot: runtimeLocalScope.scope.scopeRoot,
225233 }),
226234 key,
227235 }),
@@ -233,91 +241,91 @@ export const createPolicy = (input: { · 5 unmodified lines
233241
234242 yield* writeLocalPolicyFiles({
235243 operation: policyOps.create,
236− workspaceConfigStore,
237− workspaceStateStore,
244+ scopeConfigStore,
245+ scopeStateStore,
238246 projectConfig: {
239247 ...projectConfig,
240248 policies,
241249 },
242− workspaceState,
250+ scopeState,
243251 });
244252
245− return toLocalWorkspacePolicy({
246− workspaceId: input.workspaceId,
247− workspaceStableKey: workspacePolicyStableKey({
248− workspaceId: input.workspaceId,
249− workspaceRoot: runtimeLocalWorkspace.workspace.workspaceRoot,
253+ return toLocalScopePolicy({
254+ scopeId: input.scopeId,
255+ scopeStableKey: scopePolicyStableKey({
256+ scopeId: input.scopeId,
257+ scopeRoot: runtimeLocalScope.scope.scopeRoot,
250258 }),
251259 key,
252260 policyConfig: policies[key]!,
253− state: workspaceState.policies[key],
261+ state: scopeState.policies[key],
254262 });
255263 });
256264
257265 export const getPolicy = (input: {
258− workspaceId: WorkspaceId;
266+ scopeId: ScopeId;
259267 policyId: PolicyId;
260268 }) =>
261269 Effect.gen(function* () {
262− yield* loadWorkspacePolicyContext(policyOps.get, input.workspaceId);
263− const localWorkspace = yield* loadRuntimeLocalWorkspacePolicies(
264− input.workspaceId,
270+ yield* loadScopePolicyContext(policyOps.get, input.scopeId);
271+ const localScope = yield* loadRuntimeLocalScopePolicies(
272+ input.scopeId,
265273 ).pipe(
266274 Effect.mapError((cause) =>
267275 policyOps.get.unknownStorage(
268276 cause,
269− "Failed loading local workspace policies",
277+ "Failed loading local scope policies",
270278 ),
271279 ),
272280 );
273281 const policy =
274− localWorkspace.policies.find(
282+ localScope.policies.find(
275283 (candidate) => candidate.id === input.policyId,
276284 ) ?? null;
277285 if (policy === null) {
278286 return yield* policyOps.get.notFound(
279287 "Policy not found",
280− `workspaceId=${input.workspaceId} policyId=${input.policyId}`,
288+ `scopeId=${input.scopeId} policyId=${input.policyId}`,
281289 );
282290 }
283291 return policy;
284292 });
285293
286294 export const updatePolicy = (input: {
287− workspaceId: WorkspaceId;
295+ scopeId: ScopeId;
288296 policyId: PolicyId;
289297 payload: UpdatePolicyPayload;
290298 }) =>
291299 Effect.gen(function* () {
292− const runtimeLocalWorkspace = yield* loadWorkspacePolicyContext(
300+ const runtimeLocalScope = yield* loadScopePolicyContext(
293301 policyOps.update,
294− input.workspaceId,
302+ input.scopeId,
295303 );
296− const workspaceConfigStore = yield* WorkspaceConfigStore;
297− const workspaceStateStore = yield* WorkspaceStateStore;
298− const localWorkspace = yield* loadRuntimeLocalWorkspacePolicies(
299− input.workspaceId,
304+ const scopeConfigStore = yield* ScopeConfigStore;
305+ const scopeStateStore = yield* ScopeStateStore;
306+ const localScope = yield* loadRuntimeLocalScopePolicies(
307+ input.scopeId,
300308 ).pipe(
301309 Effect.mapError((cause) =>
302310 policyOps.update.unknownStorage(
303311 cause,
304− "Failed loading local workspace policies",
312+ "Failed loading local scope policies",
305313 ),
306314 ),
307315 );
308316 const existing =
309− localWorkspace.policies.find(
317+ localScope.policies.find(
310318 (candidate) => candidate.id === input.policyId,
311319 ) ?? null;
312320 if (existing === null) {
313321 return yield* policyOps.update.notFound(
314322 "Policy not found",
315− `workspaceId=${input.workspaceId} policyId=${input.policyId}`,
323+ `scopeId=${input.scopeId} policyId=${input.policyId}`,
316324 );
317325 }
318326
319327 const projectConfig = cloneJson(
320− localWorkspace.loadedConfig.projectConfig ?? {},
328+ localScope.loadedConfig.projectConfig ?? {},
321329 );
322330 const policies = { ...projectConfig.policies };
323331 const existingConfig = policies[existing.key]!;
@@ -345,11 +353,11 @@ export const updatePolicy = (input: { · 21 unmodified lines
345353 };
346354
347355 const updatedAt = Date.now();
348− const existingState = localWorkspace.workspaceState.policies[existing.key];
349− const workspaceState: LocalWorkspaceState = {
350− ...localWorkspace.workspaceState,
356+ const existingState = localScope.scopeState.policies[existing.key];
357+ const scopeState: LocalScopeState = {
358+ ...localScope.scopeState,
351359 policies: {
352− ...localWorkspace.workspaceState.policies,
360+ ...localScope.scopeState.policies,
353361 [existing.key]: {
354362 id: existing.id,
355363 createdAt: existingState?.createdAt ?? existing.createdAt,
@@ -360,50 +368,50 @@ export const updatePolicy = (input: { · 4 unmodified lines
360368
361369 yield* writeLocalPolicyFiles({
362370 operation: policyOps.update,
363− workspaceConfigStore,
364− workspaceStateStore,
371+ scopeConfigStore,
372+ scopeStateStore,
365373 projectConfig: {
366374 ...projectConfig,
367375 policies,
368376 },
369− workspaceState,
377+ scopeState,
370378 });
371379
372− return toLocalWorkspacePolicy({
373− workspaceId: input.workspaceId,
374− workspaceStableKey: workspacePolicyStableKey({
375− workspaceId: input.workspaceId,
376− workspaceRoot: runtimeLocalWorkspace.workspace.workspaceRoot,
380+ return toLocalScopePolicy({
381+ scopeId: input.scopeId,
382+ scopeStableKey: scopePolicyStableKey({
383+ scopeId: input.scopeId,
384+ scopeRoot: runtimeLocalScope.scope.scopeRoot,
377385 }),
378386 key: existing.key,
379387 policyConfig: policies[existing.key]!,
380− state: workspaceState.policies[existing.key],
388+ state: scopeState.policies[existing.key],
381389 });
382390 });
383391
384392 export const removePolicy = (input: {
385− workspaceId: WorkspaceId;
393+ scopeId: ScopeId;
386394 policyId: PolicyId;
387395 }) =>
388396 Effect.gen(function* () {
389− const runtimeLocalWorkspace = yield* loadWorkspacePolicyContext(
397+ const runtimeLocalScope = yield* loadScopePolicyContext(
390398 policyOps.remove,
391− input.workspaceId,
399+ input.scopeId,
392400 );
393− const workspaceConfigStore = yield* WorkspaceConfigStore;
394− const workspaceStateStore = yield* WorkspaceStateStore;
395− const localWorkspace = yield* loadRuntimeLocalWorkspacePolicies(
396− input.workspaceId,
401+ const scopeConfigStore = yield* ScopeConfigStore;
402+ const scopeStateStore = yield* ScopeStateStore;
403+ const localScope = yield* loadRuntimeLocalScopePolicies(
404+ input.scopeId,
397405 ).pipe(
398406 Effect.mapError((cause) =>
399407 policyOps.remove.unknownStorage(
400408 cause,
401− "Failed loading local workspace policies",
409+ "Failed loading local scope policies",
402410 ),
403411 ),
404412 );
405413 const existing =
406− localWorkspace.policies.find(
414+ localScope.policies.find(
407415 (candidate) => candidate.id === input.policyId,
408416 ) ?? null;
409417 if (existing === null) {
@@ -411,23 +419,23 @@ export const removePolicy = (input: { · 1 unmodified line
411419 }
412420
413421 const projectConfig = cloneJson(
414− localWorkspace.loadedConfig.projectConfig ?? {},
422+ localScope.loadedConfig.projectConfig ?? {},
415423 );
416424 const policies = { ...projectConfig.policies };
417425 delete policies[existing.key];
418426
419427 const { [existing.key]: _removedPolicy, ...remainingPolicies } =
420− localWorkspace.workspaceState.policies;
428+ localScope.scopeState.policies;
421429 yield* writeLocalPolicyFiles({
422430 operation: policyOps.remove,
423− workspaceConfigStore,
424− workspaceStateStore,
431+ scopeConfigStore,
432+ scopeStateStore,
425433 projectConfig: {
426434 ...projectConfig,
427435 policies,
428436 },
429− workspaceState: {
430− ...localWorkspace.workspaceState,
437+ scopeState: {
438+ ...localScope.scopeState,
431439 policies: remainingPolicies,
432440 },
433441 });
packages/platform/sdk/src/runtime/auth/auth-artifacts.ts+19−15
@@ -1,6 +1,8 @@
1−import type { OAuthClientProvider } from "@modelcontextprotocol/sdk/client/auth.js";
1+import type {
2+ OAuthClientProvider,
3+} from "@modelcontextprotocol/sdk/client/auth.js";
24 import {
3− type AccountId,
5+ type ScopeId,
46 AuthGrantSetJsonSchema,
57 McpOAuthAuthArtifactConfigJsonSchema,
68 McpOAuthAuthArtifactKind,
@@ -35,8 +37,10 @@ import * as Schema from "effect/Schema"; · 28 unmodified lines
3537 import type {
3638 ResolveSecretMaterial,
3739 SecretMaterialResolveContext,
38−} from "../workspace/secret-material-providers";
39−import { runtimeEffectError } from "../effect-errors";
40+} from "../scope/secret-material-providers";
41+import {
42+ runtimeEffectError,
43+} from "../effect-errors";
4044
4145 export type ResolvedSourceAuthMaterial = {
4246 placements: ReadonlyArray<RequestPlacement>;
@@ -154,7 +158,7 @@ export const authArtifactFromSourceAuth = (input: { · 111 unmodified lines
154158 source: Source;
155159 auth: SourceAuth;
156160 slot: CredentialSlot;
157− actorAccountId?: AccountId | null;
161+ actorScopeId?: ScopeId | null;
158162 existingAuthArtifactId?: AuthArtifact["id"] | null;
159163 }): AuthArtifact | null => {
160164 if (input.auth.kind === "none") {
@@ -170,9 +174,9 @@ export const authArtifactFromSourceAuth = (input: { · 9 unmodified lines
170174 if (input.auth.kind === "bearer") {
171175 return {
172176 id,
173− workspaceId: input.source.workspaceId,
177+ scopeId: input.source.scopeId,
174178 sourceId: input.source.id,
175− actorAccountId: input.actorAccountId ?? null,
179+ actorScopeId: input.actorScopeId ?? null,
176180 slot: input.slot,
177181 artifactKind: StaticBearerAuthArtifactKind,
178182 configJson: encodeStaticBearerAuthArtifactConfig({
@@ -189,9 +193,9 @@ export const authArtifactFromSourceAuth = (input: { · 10 unmodified lines
189193 if (input.auth.kind === "oauth2_authorized_user") {
190194 return {
191195 id,
192− workspaceId: input.source.workspaceId,
196+ scopeId: input.source.scopeId,
193197 sourceId: input.source.id,
194− actorAccountId: input.actorAccountId ?? null,
198+ actorScopeId: input.actorScopeId ?? null,
195199 slot: input.slot,
196200 artifactKind: RefreshableOAuth2AuthorizedUserAuthArtifactKind,
197201 configJson: encodeRefreshableOAuth2AuthorizedUserAuthArtifactConfig({
@@ -212,9 +216,9 @@ export const authArtifactFromSourceAuth = (input: { · 14 unmodified lines
212216 if (input.auth.kind === "provider_grant_ref") {
213217 return {
214218 id,
215− workspaceId: input.source.workspaceId,
219+ scopeId: input.source.scopeId,
216220 sourceId: input.source.id,
217− actorAccountId: input.actorAccountId ?? null,
221+ actorScopeId: input.actorScopeId ?? null,
218222 slot: input.slot,
219223 artifactKind: ProviderGrantRefAuthArtifactKind,
220224 configJson: encodeProviderGrantRefAuthArtifactConfig({
@@ -233,9 +237,9 @@ export const authArtifactFromSourceAuth = (input: { · 12 unmodified lines
233237 if (input.auth.kind === "mcp_oauth") {
234238 return {
235239 id,
236− workspaceId: input.source.workspaceId,
240+ scopeId: input.source.scopeId,
237241 sourceId: input.source.id,
238− actorAccountId: input.actorAccountId ?? null,
242+ actorScopeId: input.actorScopeId ?? null,
239243 slot: input.slot,
240244 artifactKind: McpOAuthAuthArtifactKind,
241245 configJson: encodeMcpOAuthAuthArtifactConfig({
@@ -259,9 +263,9 @@ export const authArtifactFromSourceAuth = (input: { · 17 unmodified lines
259263
260264 return {
261265 id,
262− workspaceId: input.source.workspaceId,
266+ scopeId: input.source.scopeId,
263267 sourceId: input.source.id,
264− actorAccountId: input.actorAccountId ?? null,
268+ actorScopeId: input.actorScopeId ?? null,
265269 slot: input.slot,
266270 artifactKind: StaticOAuth2AuthArtifactKind,
267271 configJson: encodeStaticOAuth2AuthArtifactConfig({
packages/platform/sdk/src/runtime/auth/auth-leases.ts+30−17
@@ -16,18 +16,31 @@ import { · 15 unmodified lines
1616 import * as Effect from "effect/Effect";
1717 import * as Option from "effect/Option";
1818
19−import type { ExecutorStateStoreShape } from "../executor-state-store";
20−import { resolveAuthArtifactMaterial, type ResolvedSourceAuthMaterial } from "./auth-artifacts";
19+import type {
20+ ExecutorStateStoreShape,
21+} from "../executor-state-store";
22+import {
23+ resolveAuthArtifactMaterial,
24+ type ResolvedSourceAuthMaterial,
25+} from "./auth-artifacts";
2126 import {
2227 type DeleteSecretMaterial,
2328 type ResolveSecretMaterial,
2429 type SecretMaterialResolveContext,
2530 type StoreSecretMaterial,
26−} from "../workspace/secret-material-providers";
27−import { refreshOAuth2AccessToken } from "./oauth2-pkce";
28−import type { OAuth2TokenResponse } from "./oauth2-pkce";
29−import { createPersistedMcpAuthProvider } from "./mcp-auth-provider";
30−import { runtimeEffectError } from "../effect-errors";
31+} from "../scope/secret-material-providers";
32+import {
33+ refreshOAuth2AccessToken,
34+} from "./oauth2-pkce";
35+import type {
36+ OAuth2TokenResponse,
37+} from "./oauth2-pkce";
38+import {
39+ createPersistedMcpAuthProvider,
40+} from "./mcp-auth-provider";
41+import {
42+ runtimeEffectError,
43+} from "../effect-errors";
3144
3245 const LEASE_REFRESH_SKEW_MS = 60_000;
3346
@@ -134,9 +147,9 @@ const refreshRefreshableOauth2AuthorizedUserArtifact = (input: { · 100 unmodified lines
134147 const nextLease: AuthLease = {
135148 id: input.lease?.id ?? AuthLeaseIdSchema.make(`auth_lease_${crypto.randomUUID()}`),
136149 authArtifactId: input.artifact.id,
137− workspaceId: input.artifact.workspaceId,
150+ scopeId: input.artifact.scopeId,
138151 sourceId: input.artifact.sourceId,
139− actorAccountId: input.artifact.actorAccountId,
152+ actorScopeId: input.artifact.actorScopeId,
140153 slot: input.artifact.slot,
141154 placementsTemplateJson: encodeLeasePlacementTemplatesJson([
142155 {
@@ -169,7 +182,7 @@ const refreshRefreshableOauth2AuthorizedUserArtifact = (input: { · 26 unmodified lines
169182 return nextLease;
170183 });
171184
172−const workspaceOauthClientSecretRef = (input: {
185+const scopeOauthClientSecretRef = (input: {
173186 clientSecretProviderId: string | null;
174187 clientSecretHandle: string | null;
175188 }): SecretRef | null =>
@@ -212,9 +225,9 @@ const refreshProviderGrantRefArtifact = (input: { · 36 unmodified lines
212225 }
213226 const grant = grantOption.value;
214227
215− const oauthClientOption = yield* input.executorState.workspaceOauthClients.getById(grant.oauthClientId);
228+ const oauthClientOption = yield* input.executorState.scopeOauthClients.getById(grant.oauthClientId);
216229 if (Option.isNone(oauthClientOption)) {
217− return yield* runtimeEffectError("auth/auth-leases", `Workspace OAuth client not found: ${grant.oauthClientId}`);
230+ return yield* runtimeEffectError("auth/auth-leases", `Scope OAuth client not found: ${grant.oauthClientId}`);
218231 }
219232 const oauthClient = oauthClientOption.value;
220233
@@ -222,7 +235,7 @@ const refreshProviderGrantRefArtifact = (input: { · 1 unmodified line
222235 ref: grant.refreshToken,
223236 context: input.context,
224237 });
225− const clientSecretRef = workspaceOauthClientSecretRef(oauthClient);
238+ const clientSecretRef = scopeOauthClientSecretRef(oauthClient);
226239 const clientSecret = clientSecretRef
227240 ? yield* input.resolveSecretMaterial({
228241 ref: clientSecretRef,
@@ -277,9 +290,9 @@ const refreshProviderGrantRefArtifact = (input: { · 48 unmodified lines
277290 const nextLease: AuthLease = {
278291 id: input.lease?.id ?? AuthLeaseIdSchema.make(`auth_lease_${crypto.randomUUID()}`),
279292 authArtifactId: input.artifact.id,
280− workspaceId: input.artifact.workspaceId,
293+ scopeId: input.artifact.scopeId,
281294 sourceId: input.artifact.sourceId,
282− actorAccountId: input.artifact.actorAccountId,
295+ actorScopeId: input.artifact.actorScopeId,
283296 slot: input.artifact.slot,
284297 placementsTemplateJson: encodeLeasePlacementTemplatesJson([
285298 {
@@ -363,9 +376,9 @@ export const upsertOauth2AuthorizedUserLeaseFromTokenResponse = (input: { · 77 unmodified lines
363376 const nextLease: AuthLease = {
364377 id: existingLease?.id ?? AuthLeaseIdSchema.make(`auth_lease_${crypto.randomUUID()}`),
365378 authArtifactId: input.artifact.id,
366− workspaceId: input.artifact.workspaceId,
379+ scopeId: input.artifact.scopeId,
367380 sourceId: input.artifact.sourceId,
368− actorAccountId: input.artifact.actorAccountId,
381+ actorScopeId: input.artifact.actorScopeId,
369382 slot: input.artifact.slot,
370383 placementsTemplateJson: encodeLeasePlacementTemplatesJson([
371384 {
packages/platform/sdk/src/runtime/auth/mcp-auth-provider.ts+4−2
@@ -20,13 +20,15 @@ import { · 19 unmodified lines
2020 import * as Effect from "effect/Effect";
2121 import * as Schema from "effect/Schema";
2222
23−import type { ExecutorStateStoreShape } from "../executor-state-store";
23+import type {
24+ ExecutorStateStoreShape,
25+} from "../executor-state-store";
2426 import {
2527 type DeleteSecretMaterial,
2628 type ResolveSecretMaterial,
2729 type SecretMaterialResolveContext,
2830 type StoreSecretMaterial,
29−} from "../workspace/secret-material-providers";
31+} from "../scope/secret-material-providers";
3032
3133 const encodeMcpOAuthAuthArtifactConfig = Schema.encodeSync(
3234 McpOAuthAuthArtifactConfigJsonSchema,
packages/platform/sdk/src/runtime/auth/oauth-loopback.test.ts+11−3
@@ -1,9 +1,17 @@
1−import { createServer } from "node:http";
1+import {
2+ createServer,
3+} from "node:http";
24
3−import { describe, expect, it } from "@effect/vitest";
5+import {
6+ describe,
7+ expect,
8+ it,
9+} from "@effect/vitest";
410 import * as Effect from "effect/Effect";
511
6−import { startOauthLoopbackRedirectServer } from "./oauth-loopback";
12+import {
13+ startOauthLoopbackRedirectServer,
14+} from "./oauth-loopback";
715
816 const toError = (cause: unknown): Error =>
917 cause instanceof Error ? cause : new Error(String(cause));
packages/platform/sdk/src/runtime/auth/oauth-loopback.ts+3−1
@@ -1,4 +1,6 @@
1−import { createServer } from "node:http";
1+import {
2+ createServer,
3+} from "node:http";
24
35 import * as Effect from "effect/Effect";
46
packages/platform/sdk/src/runtime/auth/provider-grant-lifecycle.ts+9−7
@@ -1,7 +1,7 @@
11 import type {
22 AuthArtifact,
33 ProviderAuthGrant,
4− WorkspaceId,
4+ ScopeId,
55 } from "#schema";
66 import {
77 decodeProviderGrantRefAuthArtifactConfig,
@@ -11,8 +11,10 @@ import * as Option from "effect/Option"; · 3 unmodified lines
1111
1212 import {
1313 type DeleteSecretMaterial,
14−} from "../workspace/secret-material-providers";
15−import type { ExecutorStateStoreShape } from "../executor-state-store";
14+} from "../scope/secret-material-providers";
15+import type {
16+ ExecutorStateStoreShape,
17+} from "../executor-state-store";
1618
1719 const providerGrantRefFromArtifact = (
1820 artifact: Pick<AuthArtifact, "artifactKind" | "configJson">,
@@ -24,11 +26,11 @@ export const providerGrantIdFromArtifact = ( · 5 unmodified lines
2426 providerGrantRefFromArtifact(artifact)?.grantId ?? null;
2527
2628 export const listProviderGrantRefArtifacts = (executorState: ExecutorStateStoreShape, input: {
27− workspaceId: WorkspaceId;
29+ scopeId: ScopeId;
2830 grantId?: ProviderAuthGrant["id"] | null;
2931 }): Effect.Effect<readonly AuthArtifact[], Error, never> =>
3032 Effect.map(
31− executorState.authArtifacts.listByWorkspaceId(input.workspaceId),
33+ executorState.authArtifacts.listByScopeId(input.scopeId),
3234 (artifacts) =>
3335 artifacts.filter((artifact) => {
3436 const grantId = providerGrantIdFromArtifact(artifact);
@@ -54,7 +56,7 @@ export const clearProviderGrantOrphanedAt = (executorState: ExecutorStateStoreSh · 19 unmodified lines
5456 });
5557
5658 export const markProviderGrantOrphanedIfUnused = (executorState: ExecutorStateStoreShape, input: {
57− workspaceId: WorkspaceId;
59+ scopeId: ScopeId;
5860 grantId: ProviderAuthGrant["id"];
5961 }): Effect.Effect<boolean, Error, never> =>
6062 Effect.gen(function* () {
@@ -64,7 +66,7 @@ export const markProviderGrantOrphanedIfUnused = (executorState: ExecutorStateSt · 3 unmodified lines
6466 }
6567
6668 const grantOption = yield* executorState.providerAuthGrants.getById(input.grantId);
67− if (Option.isNone(grantOption) || grantOption.value.workspaceId !== input.workspaceId) {
69+ if (Option.isNone(grantOption) || grantOption.value.scopeId !== input.scopeId) {
6870 return false;
6971 }
7072
packages/platform/sdk/src/runtime/auth/source-auth-material.test.ts+14−8
@@ -1,19 +1,25 @@
1−import { describe, expect, it } from "@effect/vitest";
1+import {
2+ describe,
3+ expect,
4+ it,
5+} from "@effect/vitest";
26 import * as Effect from "effect/Effect";
37 import * as Option from "effect/Option";
48
59 import {
610 AuthArtifactIdSchema,
711 SourceIdSchema,
8− WorkspaceIdSchema,
12+ ScopeIdSchema,
913 type Source,
1014 } from "#schema";
1115
12−import { resolveSourceAuthMaterialWithDeps } from "./source-auth-material";
16+import {
17+ resolveSourceAuthMaterialWithDeps,
18+} from "./source-auth-material";
1319
1420 const makeSource = (overrides: Partial<Source> = {}): Source => ({
1521 id: SourceIdSchema.make("src_tool_artifacts"),
16− workspaceId: WorkspaceIdSchema.make("ws_tool_artifacts"),
22+ scopeId: ScopeIdSchema.make("ws_tool_artifacts"),
1723 name: "GitHub",
1824 kind: "openapi",
1925 endpoint: "https://api.github.com",
@@ -176,16 +182,16 @@ describe("source-auth-material", () => { · 156 unmodified lines
176182 },
177183 auth: { kind: "none" },
178184 }),
179− actorAccountId: null,
185+ actorScopeId: null,
180186 executorState: {
181187 authArtifacts: {
182− getByWorkspaceSourceAndActor: () =>
188+ getByScopeSourceAndActor: () =>
183189 Effect.succeed(
184190 Option.some({
185191 id: AuthArtifactIdSchema.make("auth_artifact_mcp"),
186− workspaceId: WorkspaceIdSchema.make("ws_tool_artifacts"),
192+ scopeId: ScopeIdSchema.make("ws_tool_artifacts"),
187193 sourceId: SourceIdSchema.make("src_tool_artifacts"),
188− actorAccountId: null,
194+ actorScopeId: null,
189195 slot: "runtime",
190196 artifactKind: "mcp_oauth",
191197 configJson: JSON.stringify({
packages/platform/sdk/src/runtime/auth/source-auth-material.ts+20−13
@@ -1,5 +1,5 @@
11 import type {
2− AccountId,
2+ ScopeId,
33 CredentialSlot,
44 Source,
55 SourceAuth,
@@ -9,25 +9,32 @@ import * as Effect from "effect/Effect"; · 3 unmodified lines
99 import * as Layer from "effect/Layer";
1010 import * as Option from "effect/Option";
1111
12−import { ExecutorStateStore, type ExecutorStateStoreShape } from "../executor-state-store";
12+import {
13+ ExecutorStateStore,
14+ type ExecutorStateStoreShape,
15+} from "../executor-state-store";
1316 import {
1417 authArtifactFromSourceAuth,
1518 resolveAuthArtifactMaterial,
1619 type ResolvedSourceAuthMaterial,
1720 } from "./auth-artifacts";
18−import { resolveAuthArtifactMaterialWithLeases } from "./auth-leases";
21+import {
22+ resolveAuthArtifactMaterialWithLeases,
23+} from "./auth-leases";
1924 import type {
2025 DeleteSecretMaterial,
2126 ResolveSecretMaterial,
2227 SecretMaterialResolveContext,
2328 StoreSecretMaterial,
24−} from "../workspace/secret-material-providers";
29+} from "../scope/secret-material-providers";
2530 import {
2631 SecretMaterialDeleterService,
2732 SecretMaterialResolverService,
2833 SecretMaterialStorerService,
29−} from "../workspace/secret-material-providers";
30−import { runtimeEffectError } from "../effect-errors";
34+} from "../scope/secret-material-providers";
35+import {
36+ runtimeEffectError,
37+} from "../effect-errors";
3138
3239 const authForSlot = (input: {
3340 source: Source;
@@ -52,7 +59,7 @@ export type RuntimeSourceAuthMaterialShape = { · 18 unmodified lines
5259 resolve: (input: {
5360 source: Source;
5461 slot?: CredentialSlot;
55− actorAccountId?: AccountId | null;
62+ actorScopeId?: ScopeId | null;
5663 context?: SecretMaterialResolveContext;
5764 }) => Effect.Effect<ResolvedSourceAuthMaterial, Error, never>;
5865 };
@@ -64,7 +71,7 @@ export class RuntimeSourceAuthMaterialService extends Context.Tag( · 5 unmodified lines
6471 export const resolveSourceAuthMaterialWithDeps = (input: {
6572 source: Source;
6673 slot?: CredentialSlot;
67− actorAccountId?: AccountId | null;
74+ actorScopeId?: ScopeId | null;
6875 executorState?: ExecutorStateStoreShape;
6976 resolveSecretMaterial: ResolveSecretMaterial;
7077 storeSecretMaterial?: StoreSecretMaterial;
@@ -81,10 +88,10 @@ export const resolveSourceAuthMaterialWithDeps = (input: { · 10 unmodified lines
8188 : [slot] satisfies ReadonlyArray<CredentialSlot>;
8289
8390 for (const candidateSlot of candidateSlots) {
84− const artifactOption = yield* input.executorState.authArtifacts.getByWorkspaceSourceAndActor({
85− workspaceId: input.source.workspaceId,
91+ const artifactOption = yield* input.executorState.authArtifacts.getByScopeSourceAndActor({
92+ scopeId: input.source.scopeId,
8693 sourceId: input.source.id,
87− actorAccountId: input.actorAccountId ?? null,
94+ actorScopeId: input.actorScopeId ?? null,
8895 slot: candidateSlot,
8996 });
9097
@@ -108,7 +115,7 @@ export const resolveSourceAuthMaterialWithDeps = (input: { · 17 unmodified lines
108115 slot,
109116 }),
110117 slot,
111− actorAccountId: input.actorAccountId ?? null,
118+ actorScopeId: input.actorScopeId ?? null,
112119 });
113120
114121 if (input.executorState !== undefined) {
@@ -140,7 +147,7 @@ export const resolveSourceAuthMaterialWithDeps = (input: { · 25 unmodified lines
140147 export const resolveSourceAuthMaterial = (input: {
141148 source: Source;
142149 slot?: CredentialSlot;
143− actorAccountId?: AccountId | null;
150+ actorScopeId?: ScopeId | null;
144151 context?: SecretMaterialResolveContext;
145152 }): Effect.Effect<ResolvedSourceAuthMaterial, Error, RuntimeSourceAuthMaterialService> =>
146153 Effect.flatMap(RuntimeSourceAuthMaterialService, (service) => service.resolve(input));
packages/platform/sdk/src/runtime/catalog/catalog-typescript.ts+9−3
@@ -1,7 +1,13 @@
1−import { createHash } from "node:crypto";
1+import {
2+ createHash,
3+} from "node:crypto";
24
3−import type { ProjectedCatalog } from "@executor/ir/catalog";
4−import type { ShapeSymbolId } from "@executor/ir/ids";
5+import type {
6+ ProjectedCatalog,
7+} from "@executor/ir/catalog";
8+import type {
9+ ShapeSymbolId,
10+} from "@executor/ir/ids";
511 import type {
612 CatalogV1,
713 DocumentationBlock,
packages/platform/sdk/src/runtime/catalog/prettier-format.ts+3−1
@@ -1,4 +1,6 @@
1−import { format } from "prettier/standalone";
1+import {
2+ format,
3+} from "prettier/standalone";
24 import parserBabel from "prettier/plugins/babel";
35 import parserEstree from "prettier/plugins/estree";
46 import parserTypescript from "prettier/plugins/typescript";
packages/platform/sdk/src/runtime/catalog/source/reconcile.ts+24−15
@@ -1,15 +1,24 @@
11 import type {
2− AccountId,
2+ ScopeId,
33 Source,
4− WorkspaceId,
54 } from "#schema";
65 import * as Effect from "effect/Effect";
76
8−import { RuntimeLocalWorkspaceService } from "../../workspace/runtime-context";
9−import { SourceArtifactStore } from "../../workspace/storage";
10−import { getSourceAdapterForSource } from "../../sources/source-adapters";
11−import { RuntimeSourceStoreService } from "../../sources/source-store";
12−import { RuntimeSourceCatalogSyncService } from "./sync";
7+import {
8+ RuntimeLocalScopeService,
9+} from "../../scope/runtime-context";
10+import {
11+ SourceArtifactStore,
12+} from "../../scope/storage";
13+import {
14+ getSourceAdapterForSource,
15+} from "../../sources/source-adapters";
16+import {
17+ RuntimeSourceStoreService,
18+} from "../../sources/source-store";
19+import {
20+ RuntimeSourceCatalogSyncService,
21+} from "./sync";
1322
1423 const shouldReconcileSource = (source: Source): boolean =>
1524 source.enabled
@@ -17,23 +26,23 @@ const shouldReconcileSource = (source: Source): boolean => · 1 unmodified line
1726 && getSourceAdapterForSource(source).catalogKind !== "internal";
1827
1928 export const reconcileMissingSourceCatalogArtifacts = (input: {
20− workspaceId: WorkspaceId;
21− actorAccountId?: AccountId | null;
29+ scopeId: ScopeId;
30+ actorScopeId?: ScopeId | null;
2231 }): Effect.Effect<
2332 void,
2433 Error,
25− | RuntimeLocalWorkspaceService
34+ | RuntimeLocalScopeService
2635 | SourceArtifactStore
2736 | RuntimeSourceStoreService
2837 | RuntimeSourceCatalogSyncService
2938 > =>
3039 Effect.gen(function* () {
31− const runtimeLocalWorkspace = yield* RuntimeLocalWorkspaceService;
40+ const runtimeLocalScope = yield* RuntimeLocalScopeService;
3241 const sourceStore = yield* RuntimeSourceStoreService;
3342 const sourceArtifactStore = yield* SourceArtifactStore;
3443 const sourceCatalogSync = yield* RuntimeSourceCatalogSyncService;
35− const sources = yield* sourceStore.loadSourcesInWorkspace(input.workspaceId, {
36− actorAccountId: input.actorAccountId,
44+ const sources = yield* sourceStore.loadSourcesInScope(input.scopeId, {
45+ actorScopeId: input.actorScopeId,
3746 });
3847
3948 for (const source of sources) {
@@ -50,7 +59,7 @@ export const reconcileMissingSourceCatalogArtifacts = (input: { · 10 unmodified lines
5059
5160 yield* sourceCatalogSync.sync({
5261 source,
53− actorAccountId: input.actorAccountId,
62+ actorScopeId: input.actorScopeId,
5463 }).pipe(
5564 Effect.catchAll(() => Effect.void),
5665 );
@@ -58,7 +67,7 @@ export const reconcileMissingSourceCatalogArtifacts = (input: { · 1 unmodified line
5867 }).pipe(
5968 Effect.withSpan("source.catalog.reconcile_missing", {
6069 attributes: {
61− "executor.workspace.id": input.workspaceId,
70+ "executor.scope.id": input.scopeId,
6271 },
6372 }),
6473 );
packages/platform/sdk/src/runtime/catalog/source/runtime.test.ts+12−5
@@ -1,4 +1,8 @@
1−import { describe, expect, it } from "@effect/vitest";
1+import {
2+ describe,
3+ expect,
4+ it,
5+} from "@effect/vitest";
26 import * as Effect from "effect/Effect";
37
48 import type {
@@ -10,7 +14,7 @@ import { · 5 unmodified lines
1014 SourceCatalogIdSchema,
1115 SourceCatalogRevisionIdSchema,
1216 SourceIdSchema,
13− WorkspaceIdSchema,
17+ ScopeIdSchema,
1418 } from "#schema";
1519
1620 import {
@@ -27,7 +31,10 @@ import { · 10 unmodified lines
2731 ScopeIdSchema,
2832 ShapeSymbolIdSchema,
2933 } from "@executor/ir/ids";
30−import type { CatalogV1, ProvenanceRef } from "@executor/ir/model";
34+import type {
35+ CatalogV1,
36+ ProvenanceRef,
37+} from "@executor/ir/model";
3138 import {
3239 createCatalogTypeProjector,
3340 formatTypeNameSegment,
@@ -274,7 +281,7 @@ const createLoadedCatalog = (options?: { · 240 unmodified lines
274281
275282 const source = {
276283 id: SourceIdSchema.make("src_linear"),
277− workspaceId: WorkspaceIdSchema.make("ws_linear"),
284+ scopeId: ScopeIdSchema.make("ws_linear"),
278285 name: "Linear",
279286 kind: "graphql-schema",
280287 endpoint: "https://api.linear.app/graphql",
@@ -294,7 +301,7 @@ const createLoadedCatalog = (options?: { · 13 unmodified lines
294301
295302 const sourceRecord = {
296303 id: source.id,
297− workspaceId: source.workspaceId,
304+ scopeId: source.scopeId,
298305 catalogId: SourceCatalogIdSchema.make("catalog_linear"),
299306 catalogRevisionId: SourceCatalogRevisionIdSchema.make("catalog_revision_linear"),
300307 name: source.name,
packages/platform/sdk/src/runtime/catalog/source/runtime.ts+65−56
@@ -3,11 +3,10 @@ import { · 2 unmodified lines
33 type ToolDescriptor as CatalogToolDescriptor,
44 } from "@executor/codemode-core";
55 import type {
6− AccountId,
6+ ScopeId,
77 Source,
88 StoredSourceRecord,
99 StoredSourceCatalogRevisionRecord,
10− WorkspaceId,
1110 } from "#schema";
1211 import * as Context from "effect/Context";
1312 import * as Effect from "effect/Effect";
@@ -19,7 +18,9 @@ import { · 5 unmodified lines
1918 projectCatalogForAgentSdk,
2019 type ProjectedCatalog,
2120 } from "@executor/ir/catalog";
22−import type { ShapeSymbolId } from "@executor/ir/ids";
21+import type {
22+ ShapeSymbolId,
23+} from "@executor/ir/ids";
2324 import type {
2425 Capability,
2526 CatalogSnapshotV1,
@@ -27,7 +28,9 @@ import type { · 1 unmodified line
2728 Executable,
2829 ShapeSymbol,
2930 } from "@executor/ir/model";
30−import { LocalSourceArtifactMissingError } from "../../workspace-errors";
31+import {
32+ LocalSourceArtifactMissingError,
33+} from "../../scope-errors";
3134 import {
3235 createCatalogTypeProjector,
3336 documentationComment,
@@ -36,21 +39,27 @@ import { · 2 unmodified lines
3639 shapeAllowsOmittedArgs,
3740 type CatalogTypeProjector,
3841 } from "../catalog-typescript";
39−import { formatWithPrettier } from "../prettier-format";
4042 import {
41− RuntimeLocalWorkspaceService,
42− type RuntimeLocalWorkspaceState,
43−} from "../../workspace/runtime-context";
44−import type { LocalSourceArtifact } from "../../source-artifacts";
43+ formatWithPrettier,
44+} from "../prettier-format";
45+import {
46+ RuntimeLocalScopeService,
47+ type RuntimeLocalScopeState,
48+} from "../../scope/runtime-context";
49+import type {
50+ LocalSourceArtifact,
51+} from "../../source-artifacts";
4552 import {
4653 SourceArtifactStore,
4754 type SourceArtifactStoreShape,
48−} from "../../workspace/storage";
55+} from "../../scope/storage";
4956 import {
5057 RuntimeSourceStoreService,
5158 type RuntimeSourceStore,
5259 } from "../../sources/source-store";
53−import { runtimeEffectError } from "../../effect-errors";
60+import {
61+ runtimeEffectError,
62+} from "../../effect-errors";
5463
5564 type CatalogImportMetadata = CatalogSnapshotV1["import"];
5665
@@ -644,7 +653,7 @@ const sourceRecordFromCatalogArtifact = (input: { · 587 unmodified lines
644653 };
645654 }): StoredSourceRecord => ({
646655 id: input.source.id,
647− workspaceId: input.source.workspaceId,
656+ scopeId: input.source.scopeId,
648657 catalogId: input.artifact.catalogId,
649658 catalogRevisionId: input.artifact.revision.id,
650659 name: input.source.name,
@@ -663,23 +672,23 @@ const sourceRecordFromCatalogArtifact = (input: { · 12 unmodified lines
663672
664673 type RuntimeSourceCatalogStoreShape = {
665674 loadWorkspaceSourceCatalogs: (input: {
666− workspaceId: WorkspaceId;
667− actorAccountId?: AccountId | null;
675+ scopeId: ScopeId;
676+ actorScopeId?: ScopeId | null;
668677 }) => Effect.Effect<readonly LoadedSourceCatalog[], Error, never>;
669678 loadSourceWithCatalog: (input: {
670− workspaceId: WorkspaceId;
679+ scopeId: ScopeId;
671680 sourceId: Source["id"];
672− actorAccountId?: AccountId | null;
681+ actorScopeId?: ScopeId | null;
673682 }) => Effect.Effect<LoadedSourceCatalog, Error | LocalSourceArtifactMissingError, never>;
674683 loadWorkspaceSourceCatalogToolIndex: (input: {
675− workspaceId: WorkspaceId;
676− actorAccountId?: AccountId | null;
684+ scopeId: ScopeId;
685+ actorScopeId?: ScopeId | null;
677686 includeSchemas: boolean;
678687 }) => Effect.Effect<readonly LoadedSourceCatalogToolIndexEntry[], Error, never>;
679688 loadWorkspaceSourceCatalogToolByPath: (input: {
680− workspaceId: WorkspaceId;
689+ scopeId: ScopeId;
681690 path: string;
682− actorAccountId?: AccountId | null;
691+ actorScopeId?: ScopeId | null;
683692 includeSchemas: boolean;
684693 }) => Effect.Effect<LoadedSourceCatalogToolIndexEntry | null, Error, never>;
685694 };
@@ -691,25 +700,25 @@ export class RuntimeSourceCatalogStoreService extends Context.Tag( · 5 unmodified lines
691700 )<RuntimeSourceCatalogStoreService, RuntimeSourceCatalogStoreShape>() {}
692701
693702 type RuntimeSourceCatalogStoreDeps = {
694− runtimeLocalWorkspace: RuntimeLocalWorkspaceState;
703+ runtimeLocalScope: RuntimeLocalScopeState;
695704 sourceStore: RuntimeSourceStore;
696705 sourceArtifactStore: SourceArtifactStoreShape;
697706 };
698707
699708 type SourceCatalogRuntimeServices =
700− | RuntimeLocalWorkspaceService
709+ | RuntimeLocalScopeService
701710 | RuntimeSourceStoreService
702711 | SourceArtifactStore;
703712
704713 const ensureRuntimeCatalogWorkspace = (
705714 deps: RuntimeSourceCatalogStoreDeps,
706− workspaceId: WorkspaceId,
715+ scopeId: ScopeId,
707716 ) =>
708717 Effect.gen(function* () {
709− if (deps.runtimeLocalWorkspace.installation.workspaceId !== workspaceId) {
718+ if (deps.runtimeLocalScope.installation.scopeId !== scopeId) {
710719 return yield* Effect.fail(
711720 runtimeEffectError("catalog/source/runtime",
712− `Runtime local workspace mismatch: expected ${workspaceId}, got ${deps.runtimeLocalWorkspace.installation.workspaceId}`,
721+ `Runtime local scope mismatch: expected ${scopeId}, got ${deps.runtimeLocalScope.installation.scopeId}`,
713722 ),
714723 );
715724 }
@@ -723,15 +732,15 @@ const buildSnapshotFromArtifact = (input: { · 7 unmodified lines
723732 };
724733
725734 const loadWorkspaceSourceCatalogsWithDeps = (deps: RuntimeSourceCatalogStoreDeps, input: {
726− workspaceId: WorkspaceId;
727− actorAccountId?: AccountId | null;
735+ scopeId: ScopeId;
736+ actorScopeId?: ScopeId | null;
728737 }): Effect.Effect<readonly LoadedSourceCatalog[], Error, never> =>
729738 Effect.gen(function* () {
730− yield* ensureRuntimeCatalogWorkspace(deps, input.workspaceId);
731− const sources = yield* deps.sourceStore.loadSourcesInWorkspace(
732− input.workspaceId,
739+ yield* ensureRuntimeCatalogWorkspace(deps, input.scopeId);
740+ const sources = yield* deps.sourceStore.loadSourcesInScope(
741+ input.scopeId,
733742 {
734− actorAccountId: input.actorAccountId,
743+ actorScopeId: input.actorScopeId,
735744 },
736745 );
737746
@@ -773,16 +782,16 @@ const loadWorkspaceSourceCatalogsWithDeps = (deps: RuntimeSourceCatalogStoreDeps · 35 unmodified lines
773782 });
774783
775784 const loadSourceWithCatalogWithDeps = (deps: RuntimeSourceCatalogStoreDeps, input: {
776− workspaceId: WorkspaceId;
785+ scopeId: ScopeId;
777786 sourceId: Source["id"];
778− actorAccountId?: AccountId | null;
787+ actorScopeId?: ScopeId | null;
779788 }): Effect.Effect<LoadedSourceCatalog, Error | LocalSourceArtifactMissingError, never> =>
780789 Effect.gen(function* () {
781− yield* ensureRuntimeCatalogWorkspace(deps, input.workspaceId);
790+ yield* ensureRuntimeCatalogWorkspace(deps, input.scopeId);
782791 const source = yield* deps.sourceStore.loadSourceById({
783− workspaceId: input.workspaceId,
792+ scopeId: input.scopeId,
784793 sourceId: input.sourceId,
785− actorAccountId: input.actorAccountId,
794+ actorScopeId: input.actorScopeId,
786795 });
787796 const artifact = yield* deps.sourceArtifactStore.read({
788797 sourceId: source.id,
@@ -819,17 +828,17 @@ const loadSourceWithCatalogWithDeps = (deps: RuntimeSourceCatalogStoreDeps, inpu · 30 unmodified lines
819828 });
820829
821830 export const loadWorkspaceSourceCatalogs = (input: {
822− workspaceId: WorkspaceId;
823− actorAccountId?: AccountId | null;
831+ scopeId: ScopeId;
832+ actorScopeId?: ScopeId | null;
824833 }): Effect.Effect<readonly LoadedSourceCatalog[], Error, SourceCatalogRuntimeServices> =>
825834 Effect.gen(function* () {
826− const runtimeLocalWorkspace = yield* RuntimeLocalWorkspaceService;
835+ const runtimeLocalScope = yield* RuntimeLocalScopeService;
827836 const sourceStore = yield* RuntimeSourceStoreService;
828837 const sourceArtifactStore = yield* SourceArtifactStore;
829838
830839 return yield* loadWorkspaceSourceCatalogsWithDeps(
831840 {
832− runtimeLocalWorkspace,
841+ runtimeLocalScope,
833842 sourceStore,
834843 sourceArtifactStore,
835844 },
@@ -838,22 +847,22 @@ export const loadWorkspaceSourceCatalogs = (input: { · 2 unmodified lines
838847 });
839848
840849 export const loadSourceWithCatalog = (input: {
841− workspaceId: WorkspaceId;
850+ scopeId: ScopeId;
842851 sourceId: Source["id"];
843− actorAccountId?: AccountId | null;
852+ actorScopeId?: ScopeId | null;
844853 }): Effect.Effect<
845854 LoadedSourceCatalog,
846855 Error | LocalSourceArtifactMissingError,
847856 SourceCatalogRuntimeServices
848857 > =>
849858 Effect.gen(function* () {
850− const runtimeLocalWorkspace = yield* RuntimeLocalWorkspaceService;
859+ const runtimeLocalScope = yield* RuntimeLocalScopeService;
851860 const sourceStore = yield* RuntimeSourceStoreService;
852861 const sourceArtifactStore = yield* SourceArtifactStore;
853862
854863 return yield* loadSourceWithCatalogWithDeps(
855864 {
856− runtimeLocalWorkspace,
865+ runtimeLocalScope,
857866 sourceStore,
858867 sourceArtifactStore,
859868 },
@@ -1096,8 +1105,8 @@ export const expandCatalogToolByPath = (input: { · 236 unmodified lines
10961105 );
10971106
10981107 export const loadWorkspaceSourceCatalogToolIndex = (input: {
1099− workspaceId: WorkspaceId;
1100− actorAccountId?: AccountId | null;
1108+ scopeId: ScopeId;
1109+ actorScopeId?: ScopeId | null;
11011110 includeSchemas: boolean;
11021111 }): Effect.Effect<
11031112 readonly LoadedSourceCatalogToolIndexEntry[],
@@ -1106,8 +1115,8 @@ export const loadWorkspaceSourceCatalogToolIndex = (input: { · 2 unmodified lines
11061115 > =>
11071116 Effect.gen(function* () {
11081117 const catalogs = yield* loadWorkspaceSourceCatalogs({
1109− workspaceId: input.workspaceId,
1110− actorAccountId: input.actorAccountId,
1118+ scopeId: input.scopeId,
1119+ actorScopeId: input.actorScopeId,
11111120 });
11121121 const tools = yield* expandCatalogTools({
11131122 catalogs,
@@ -1129,9 +1138,9 @@ export const loadWorkspaceSourceCatalogToolIndex = (input: { · 15 unmodified lines
11291138 });
11301139
11311140 export const loadWorkspaceSourceCatalogToolByPath = (input: {
1132− workspaceId: WorkspaceId;
1141+ scopeId: ScopeId;
11331142 path: string;
1134− actorAccountId?: AccountId | null;
1143+ actorScopeId?: ScopeId | null;
11351144 includeSchemas: boolean;
11361145 }): Effect.Effect<
11371146 LoadedSourceCatalogToolIndexEntry | null,
@@ -1140,8 +1149,8 @@ export const loadWorkspaceSourceCatalogToolByPath = (input: { · 2 unmodified lines
11401149 > =>
11411150 Effect.gen(function* () {
11421151 const catalogs = yield* loadWorkspaceSourceCatalogs({
1143− workspaceId: input.workspaceId,
1144− actorAccountId: input.actorAccountId,
1152+ scopeId: input.scopeId,
1153+ actorScopeId: input.actorScopeId,
11451154 });
11461155 const tool = yield* expandCatalogToolByPath({
11471156 catalogs,
@@ -1168,12 +1177,12 @@ export const loadWorkspaceSourceCatalogToolByPath = (input: { · 20 unmodified lines
11681177 export const RuntimeSourceCatalogStoreLive = Layer.effect(
11691178 RuntimeSourceCatalogStoreService,
11701179 Effect.gen(function* () {
1171− const runtimeLocalWorkspace = yield* RuntimeLocalWorkspaceService;
1180+ const runtimeLocalScope = yield* RuntimeLocalScopeService;
11721181 const sourceStore = yield* RuntimeSourceStoreService;
11731182 const sourceArtifactStore = yield* SourceArtifactStore;
11741183
11751184 const deps: RuntimeSourceCatalogStoreDeps = {
1176− runtimeLocalWorkspace,
1185+ runtimeLocalScope,
11771186 sourceStore,
11781187 sourceArtifactStore,
11791188 };
@@ -1185,13 +1194,13 @@ export const RuntimeSourceCatalogStoreLive = Layer.effect( · 5 unmodified lines
11851194 loadSourceWithCatalogWithDeps(deps, input),
11861195 loadWorkspaceSourceCatalogToolIndex: (input) =>
11871196 loadWorkspaceSourceCatalogToolIndex(input).pipe(
1188− Effect.provideService(RuntimeLocalWorkspaceService, runtimeLocalWorkspace),
1197+ Effect.provideService(RuntimeLocalScopeService, runtimeLocalScope),
11891198 Effect.provideService(RuntimeSourceStoreService, sourceStore),
11901199 Effect.provideService(SourceArtifactStore, sourceArtifactStore),
11911200 ),
11921201 loadWorkspaceSourceCatalogToolByPath: (input) =>
11931202 loadWorkspaceSourceCatalogToolByPath(input).pipe(
1194− Effect.provideService(RuntimeLocalWorkspaceService, runtimeLocalWorkspace),
1203+ Effect.provideService(RuntimeLocalScopeService, runtimeLocalScope),
11951204 Effect.provideService(RuntimeSourceStoreService, sourceStore),
11961205 Effect.provideService(SourceArtifactStore, sourceArtifactStore),
11971206 ),
packages/platform/sdk/src/runtime/catalog/source/snapshot.test.ts+21−7
@@ -1,20 +1,34 @@
1−import { describe, expect, it } from "@effect/vitest";
1+import {
2+ describe,
3+ expect,
4+ it,
5+} from "@effect/vitest";
26
3−import type { Source } from "#schema";
7+import type {
8+ Source,
9+} from "#schema";
410
511 import {
612 buildGraphqlToolPresentation,
713 compileGraphqlToolDefinitions,
814 type GraphqlToolManifest,
915 } from "@executor/source-graphql";
10−import { createGoogleDiscoveryCatalogSnapshot } from "@executor/source-google-discovery";
11−import { createGraphqlCatalogSnapshot } from "@executor/source-graphql";
12−import { createMcpCatalogSnapshot } from "@executor/source-mcp";
13−import { createOpenApiCatalogSnapshot } from "@executor/source-openapi";
16+import {
17+ createGoogleDiscoveryCatalogSnapshot,
18+} from "@executor/source-google-discovery";
19+import {
20+ createGraphqlCatalogSnapshot,
21+} from "@executor/source-graphql";
22+import {
23+ createMcpCatalogSnapshot,
24+} from "@executor/source-mcp";
25+import {
26+ createOpenApiCatalogSnapshot,
27+} from "@executor/source-openapi";
1428
1529 const baseSource: Source = {
1630 id: "src_calendar" as Source["id"],
17− workspaceId: "ws_test" as Source["workspaceId"],
31+ scopeId: "ws_test" as Source["scopeId"],
1832 name: "Calendar",
1933 kind: "openapi",
2034 endpoint: "https://api.example.test",
packages/platform/sdk/src/runtime/catalog/source/sync.ts+53−45
@@ -1,26 +1,28 @@
11 import type {
2− AccountId,
2+ ScopeId,
33 Source,
44 SourceStatus,
55 } from "#schema";
6−import type { McpToolManifest } from "@executor/source-mcp";
6+import type {
7+ McpToolManifest,
8+} from "@executor/source-mcp";
79 import * as Context from "effect/Context";
810 import * as Effect from "effect/Effect";
911 import * as Layer from "effect/Layer";
1012
1113 import {
12− RuntimeLocalWorkspaceService,
13− type RuntimeLocalWorkspaceState,
14−} from "../../workspace/runtime-context";
14+ RuntimeLocalScopeService,
15+ type RuntimeLocalScopeState,
16+} from "../../scope/runtime-context";
1517 import {
1618 SourceArtifactStore,
1719 type SourceArtifactStoreShape,
18− WorkspaceStateStore,
19− type WorkspaceStateStoreShape,
20−} from "../../workspace/storage";
20+ ScopeStateStore,
21+ type ScopeStateStoreShape,
22+} from "../../scope/storage";
2123 import {
22− type LocalWorkspaceState,
23−} from "../../workspace-state";
24+ type LocalScopeState,
25+} from "../../scope-state";
2426 import {
2527 RuntimeSourceAuthMaterialService,
2628 } from "../../auth/source-auth-material";
@@ -30,13 +32,19 @@ import { · 3 unmodified lines
3032 import {
3133 catalogSyncResultFromMcpManifest,
3234 } from "@executor/source-mcp";
33−import { SecretMaterialResolverService } from "../../workspace/secret-material-providers";
34−import { snapshotFromSourceCatalogSyncResult } from "@executor/source-core";
35+import {
36+ SecretMaterialResolverService,
37+} from "../../scope/secret-material-providers";
38+import {
39+ snapshotFromSourceCatalogSyncResult,
40+} from "@executor/source-core";
3541 import {
3642 SourceTypeDeclarationsRefresherService,
3743 type SourceTypeDeclarationsRefresherShape,
3844 } from "./type-declarations";
39−import { runtimeEffectError } from "../../effect-errors";
45+import {
46+ runtimeEffectError,
47+} from "../../effect-errors";
4048
4149 const shouldIndexSource = (source: Source): boolean =>
4250 source.enabled
@@ -44,8 +52,8 @@ const shouldIndexSource = (source: Source): boolean => · 1 unmodified line
4452 && getSourceAdapterForSource(source).catalogKind !== "internal";
4553
4654 type RuntimeSourceCatalogSyncDeps = {
47− runtimeLocalWorkspace: RuntimeLocalWorkspaceState;
48− workspaceStateStore: WorkspaceStateStoreShape;
55+ runtimeLocalScope: RuntimeLocalScopeState;
56+ scopeStateStore: ScopeStateStoreShape;
4957 sourceArtifactStore: SourceArtifactStoreShape;
5058 sourceTypeDeclarationsRefresher: SourceTypeDeclarationsRefresherShape;
5159 resolveSecretMaterial: Effect.Effect.Success<typeof SecretMaterialResolverService>;
@@ -53,8 +61,8 @@ type RuntimeSourceCatalogSyncDeps = { · 1 unmodified line
5361 };
5462
5563 type SourceCatalogSyncServices =
56− | RuntimeLocalWorkspaceService
57− | WorkspaceStateStore
64+ | RuntimeLocalScopeService
65+ | ScopeStateStore
5866 | SourceArtifactStore
5967 | SourceTypeDeclarationsRefresherService
6068 | RuntimeSourceAuthMaterialService
@@ -63,7 +71,7 @@ type SourceCatalogSyncServices = · 2 unmodified lines
6371 export type RuntimeSourceCatalogSyncShape = {
6472 sync: (input: {
6573 source: Source;
66− actorAccountId?: AccountId | null;
74+ actorScopeId?: ScopeId | null;
6775 }) => Effect.Effect<void, Error, never>;
6876 persistMcpCatalogSnapshotFromManifest: (input: {
6977 source: Source;
@@ -77,13 +85,13 @@ export class RuntimeSourceCatalogSyncService extends Context.Tag( · 7 unmodified lines
7785
7886 const ensureRuntimeCatalogSyncWorkspace = (
7987 deps: RuntimeSourceCatalogSyncDeps,
80− workspaceId: Source["workspaceId"],
88+ scopeId: Source["scopeId"],
8189 ) =>
8290 Effect.gen(function* () {
83− if (deps.runtimeLocalWorkspace.installation.workspaceId !== workspaceId) {
91+ if (deps.runtimeLocalScope.installation.scopeId !== scopeId) {
8492 return yield* Effect.fail(
8593 runtimeEffectError("catalog/source/sync",
86− `Runtime local workspace mismatch: expected ${workspaceId}, got ${deps.runtimeLocalWorkspace.installation.workspaceId}`,
94+ `Runtime local scope mismatch: expected ${scopeId}, got ${deps.runtimeLocalScope.installation.scopeId}`,
8795 ),
8896 );
8997 }
@@ -93,16 +101,16 @@ const syncSourceCatalogWithDeps = ( · 3 unmodified lines
93101 deps: RuntimeSourceCatalogSyncDeps,
94102 input: {
95103 source: Source;
96− actorAccountId?: AccountId | null;
104+ actorScopeId?: ScopeId | null;
97105 },
98106 ): Effect.Effect<void, Error, never> =>
99107 Effect.gen(function* () {
100− yield* ensureRuntimeCatalogSyncWorkspace(deps, input.source.workspaceId);
108+ yield* ensureRuntimeCatalogSyncWorkspace(deps, input.source.scopeId);
101109
102110 if (!shouldIndexSource(input.source)) {
103− const state = yield* deps.workspaceStateStore.load();
111+ const state = yield* deps.scopeStateStore.load();
104112 const existingSourceState = state.sources[input.source.id];
105− const nextState: LocalWorkspaceState = {
113+ const nextState: LocalScopeState = {
106114 ...state,
107115 sources: {
108116 ...state.sources,
@@ -115,7 +123,7 @@ const syncSourceCatalogWithDeps = ( · 6 unmodified lines
115123 },
116124 },
117125 };
118− yield* deps.workspaceStateStore.write({
126+ yield* deps.scopeStateStore.write({
119127 state: nextState,
120128 });
121129 yield* deps.sourceTypeDeclarationsRefresher.refreshSourceInBackground({
@@ -133,7 +141,7 @@ const syncSourceCatalogWithDeps = ( · 11 unmodified lines
133141 deps.sourceAuthMaterialService.resolve({
134142 source: input.source,
135143 slot,
136− actorAccountId: input.actorAccountId,
144+ actorScopeId: input.actorScopeId,
137145 }),
138146 });
139147 const snapshot = snapshotFromSourceCatalogSyncResult(syncResult);
@@ -145,9 +153,9 @@ const syncSourceCatalogWithDeps = ( · 5 unmodified lines
145153 }),
146154 });
147155
148− const state = yield* deps.workspaceStateStore.load();
156+ const state = yield* deps.scopeStateStore.load();
149157 const existingSourceState = state.sources[input.source.id];
150− const nextState: LocalWorkspaceState = {
158+ const nextState: LocalScopeState = {
151159 ...state,
152160 sources: {
153161 ...state.sources,
@@ -160,7 +168,7 @@ const syncSourceCatalogWithDeps = ( · 6 unmodified lines
160168 },
161169 },
162170 };
163− yield* deps.workspaceStateStore.write({
171+ yield* deps.scopeStateStore.write({
164172 state: nextState,
165173 });
166174
@@ -187,7 +195,7 @@ const persistMcpCatalogSnapshotFromManifestWithDeps = ( · 20 unmodified lines
187195 },
188196 ): Effect.Effect<void, Error, never> =>
189197 Effect.gen(function* () {
190− yield* ensureRuntimeCatalogSyncWorkspace(deps, input.source.workspaceId);
198+ yield* ensureRuntimeCatalogSyncWorkspace(deps, input.source.scopeId);
191199 const syncResult = catalogSyncResultFromMcpManifest({
192200 source: input.source,
193201 endpoint: input.source.endpoint,
@@ -211,11 +219,11 @@ const persistMcpCatalogSnapshotFromManifestWithDeps = ( · 17 unmodified lines
211219
212220 export const syncSourceCatalog = (input: {
213221 source: Source;
214− actorAccountId?: AccountId | null;
222+ actorScopeId?: ScopeId | null;
215223 }): Effect.Effect<void, Error, SourceCatalogSyncServices> =>
216224 Effect.gen(function* () {
217− const runtimeLocalWorkspace = yield* RuntimeLocalWorkspaceService;
218− const workspaceStateStore = yield* WorkspaceStateStore;
225+ const runtimeLocalScope = yield* RuntimeLocalScopeService;
226+ const scopeStateStore = yield* ScopeStateStore;
219227 const sourceArtifactStore = yield* SourceArtifactStore;
220228 const sourceTypeDeclarationsRefresher =
221229 yield* SourceTypeDeclarationsRefresherService;
@@ -224,8 +232,8 @@ export const syncSourceCatalog = (input: { · 2 unmodified lines
224232
225233 return yield* syncSourceCatalogWithDeps(
226234 {
227− runtimeLocalWorkspace,
228− workspaceStateStore,
235+ runtimeLocalScope,
236+ scopeStateStore,
229237 sourceArtifactStore,
230238 sourceTypeDeclarationsRefresher,
231239 resolveSecretMaterial,
@@ -233,7 +241,7 @@ export const syncSourceCatalog = (input: { · 1 unmodified line
233241 },
234242 {
235243 source: input.source,
236− actorAccountId: input.actorAccountId,
244+ actorScopeId: input.actorScopeId,
237245 },
238246 );
239247 });
@@ -243,8 +251,8 @@ export const persistMcpCatalogSnapshotFromManifest = (input: { · 3 unmodified lines
243251 manifest: McpToolManifest;
244252 }): Effect.Effect<void, Error, SourceCatalogSyncServices> =>
245253 Effect.gen(function* () {
246− const runtimeLocalWorkspace = yield* RuntimeLocalWorkspaceService;
247− const workspaceStateStore = yield* WorkspaceStateStore;
254+ const runtimeLocalScope = yield* RuntimeLocalScopeService;
255+ const scopeStateStore = yield* ScopeStateStore;
248256 const sourceArtifactStore = yield* SourceArtifactStore;
249257 const sourceTypeDeclarationsRefresher =
250258 yield* SourceTypeDeclarationsRefresherService;
@@ -253,8 +261,8 @@ export const persistMcpCatalogSnapshotFromManifest = (input: { · 2 unmodified lines
253261
254262 return yield* persistMcpCatalogSnapshotFromManifestWithDeps(
255263 {
256− runtimeLocalWorkspace,
257− workspaceStateStore,
264+ runtimeLocalScope,
265+ scopeStateStore,
258266 sourceArtifactStore,
259267 sourceTypeDeclarationsRefresher,
260268 resolveSecretMaterial,
@@ -267,8 +275,8 @@ export const persistMcpCatalogSnapshotFromManifest = (input: { · 6 unmodified lines
267275 export const RuntimeSourceCatalogSyncLive = Layer.effect(
268276 RuntimeSourceCatalogSyncService,
269277 Effect.gen(function* () {
270− const runtimeLocalWorkspace = yield* RuntimeLocalWorkspaceService;
271− const workspaceStateStore = yield* WorkspaceStateStore;
278+ const runtimeLocalScope = yield* RuntimeLocalScopeService;
279+ const scopeStateStore = yield* ScopeStateStore;
272280 const sourceArtifactStore = yield* SourceArtifactStore;
273281 const sourceTypeDeclarationsRefresher =
274282 yield* SourceTypeDeclarationsRefresherService;
@@ -276,8 +284,8 @@ export const RuntimeSourceCatalogSyncLive = Layer.effect( · 1 unmodified line
276284 const sourceAuthMaterialService = yield* RuntimeSourceAuthMaterialService;
277285
278286 const deps: RuntimeSourceCatalogSyncDeps = {
279− runtimeLocalWorkspace,
280− workspaceStateStore,
287+ runtimeLocalScope,
288+ scopeStateStore,
281289 sourceArtifactStore,
282290 sourceTypeDeclarationsRefresher,
283291 resolveSecretMaterial,
packages/platform/sdk/src/runtime/catalog/source/type-declarations.ts+6−2
@@ -1,5 +1,9 @@
1−import type { Source } from "#schema";
2−import type { CatalogSnapshotV1 } from "@executor/ir/model";
1+import type {
2+ Source,
3+} from "#schema";
4+import type {
5+ CatalogSnapshotV1,
6+} from "@executor/ir/model";
37 import * as Context from "effect/Context";
48 import * as Effect from "effect/Effect";
59
packages/platform/sdk/src/runtime/execution/http.test.ts+39−19
@@ -1,16 +1,36 @@
1−import { tmpdir } from "node:os";
2−import { join } from "node:path";
3−import { FileSystem } from "@effect/platform";
4−import { NodeFileSystem } from "@effect/platform-node";
5−import { describe, expect, it } from "@effect/vitest";
1+import {
2+ tmpdir,
3+} from "node:os";
4+import {
5+ join,
6+} from "node:path";
7+import {
8+ FileSystem,
9+} from "@effect/platform";
10+import {
11+ NodeFileSystem,
12+} from "@effect/platform-node";
13+import {
14+ describe,
15+ expect,
16+ it,
17+} from "@effect/vitest";
618 import * as Effect from "effect/Effect";
719 import * as Schema from "effect/Schema";
820
9−import { makeToolInvokerFromTools } from "@executor/codemode-core";
10−import { makeDenoSubprocessExecutor } from "@executor/runtime-deno-subprocess";
21+import {
22+ makeToolInvokerFromTools,
23+} from "@executor/codemode-core";
24+import {
25+ makeDenoSubprocessExecutor,
26+} from "@executor/runtime-deno-subprocess";
1127
12−import { createLocalExecutorRuntime as createExecutorRuntime } from "../../../../sdk-file/src/index";
13−import { withExecutorApiClient } from "./test-http-client";
28+import {
29+ createLocalExecutorRuntime as createExecutorRuntime,
30+} from "../../../../sdk-file/src/index";
31+import {
32+ withExecutorApiClient,
33+} from "./test-http-client";
1434
1535 const makeExecutionResolver = () => {
1636 const toolInvoker = makeToolInvokerFromTools({
@@ -41,22 +61,22 @@ const makeExecutionResolver = () => { · 24 unmodified lines
4161 const makeRuntime = Effect.acquireRelease(
4262 Effect.gen(function* () {
4363 const fs = yield* FileSystem.FileSystem;
44− const workspaceRoot = yield* fs.makeTempDirectory({
64+ const scopeRoot = yield* fs.makeTempDirectory({
4565 directory: tmpdir(),
4666 prefix: "executor-execution-http-",
4767 });
4868
4969 return {
50− workspaceRoot,
51− homeConfigPath: join(workspaceRoot, ".executor-home.jsonc"),
52− homeStateDirectory: join(workspaceRoot, ".executor-home-state"),
70+ scopeRoot,
71+ homeConfigPath: join(scopeRoot, ".executor-home.jsonc"),
72+ homeStateDirectory: join(scopeRoot, ".executor-home-state"),
5373 };
5474 }).pipe(
5575 Effect.provide(NodeFileSystem.layer),
56− Effect.flatMap(({ workspaceRoot, homeConfigPath, homeStateDirectory }) =>
76+ Effect.flatMap(({ scopeRoot, homeConfigPath, homeStateDirectory }) =>
5777 createExecutorRuntime({
5878 localDataDir: ":memory:",
59− workspaceRoot,
79+ workspaceRoot: scopeRoot,
6080 homeConfigPath,
6181 homeStateDirectory,
6282 executionResolver: makeExecutionResolver(),
@@ -75,12 +95,12 @@ describe("execution-http", () => { · 12 unmodified lines
7595 const createExecution = yield* withExecutorApiClient(
7696 {
7797 runtime,
78− accountId: installation.accountId,
98+ actorScopeId: installation.actorScopeId,
7999 },
80100 (client) =>
81101 client.executions.create({
82102 path: {
83− workspaceId: installation.workspaceId,
103+ scopeId: installation.scopeId,
84104 },
85105 payload: {
86106 code: "return await tools.math.add({ a: 20, b: 22 });",
@@ -95,12 +115,12 @@ describe("execution-http", () => { · 8 unmodified lines
95115 const getExecution = yield* withExecutorApiClient(
96116 {
97117 runtime,
98− accountId: installation.accountId,
118+ actorScopeId: installation.actorScopeId,
99119 },
100120 (client) =>
101121 client.executions.get({
102122 path: {
103− workspaceId: installation.workspaceId,
123+ scopeId: installation.scopeId,
104124 executionId: createExecution.execution.id,
105125 },
106126 }),
packages/platform/sdk/src/runtime/execution/ir-execution.ts+21−8
@@ -1,11 +1,24 @@
1−import type { AccountId, Source } from "#schema";
2−import type { OnElicitation } from "@executor/codemode-core";
1+import type {
2+ ScopeId,
3+ Source,
4+} from "#schema";
5+import type {
6+ OnElicitation,
7+} from "@executor/codemode-core";
38 import * as Effect from "effect/Effect";
49
5−import type { LoadedSourceCatalogToolIndexEntry } from "../catalog/source/runtime";
6−import type { ResolvedSourceAuthMaterial } from "../auth/source-auth-material";
7−import { getSourceAdapter } from "../sources/source-adapters";
8−import { runtimeEffectError } from "../effect-errors";
10+import type {
11+ LoadedSourceCatalogToolIndexEntry,
12+} from "../catalog/source/runtime";
13+import type {
14+ ResolvedSourceAuthMaterial,
15+} from "../auth/source-auth-material";
16+import {
17+ getSourceAdapter,
18+} from "../sources/source-adapters";
19+import {
20+ runtimeEffectError,
21+} from "../effect-errors";
922
1023 export const invocationDescriptorFromTool = (input: {
1124 tool: LoadedSourceCatalogToolIndexEntry;
@@ -39,8 +52,8 @@ export const invocationDescriptorFromTool = (input: { · 27 unmodified lines
3952 });
4053
4154 export const invokeIrTool = (input: {
42− workspaceId: Source["workspaceId"];
43− accountId: AccountId;
55+ scopeId: Source["scopeId"];
56+ actorScopeId: ScopeId;
4457 tool: LoadedSourceCatalogToolIndexEntry;
4558 auth: ResolvedSourceAuthMaterial;
4659 args: unknown;
packages/platform/sdk/src/runtime/execution/live.test.ts+8−2
@@ -1,6 +1,12 @@
1−import { describe, expect, it } from "@effect/vitest";
1+import {
2+ describe,
3+ expect,
4+ it,
5+} from "@effect/vitest";
26
3−import { sanitizePersistedElicitationResponse } from "./live";
7+import {
8+ sanitizePersistedElicitationResponse,
9+} from "./live";
410
511 describe("live-execution", () => {
612 it("redacts secret refs from persisted interaction responses", () => {
packages/platform/sdk/src/runtime/execution/live.ts+6−2
@@ -2,7 +2,9 @@ import type { · 1 unmodified line
22 ElicitationResponse,
33 OnElicitation,
44 } from "@executor/codemode-core";
5−import { clearMcpConnectionPoolRun } from "@executor/source-mcp";
5+import {
6+ clearMcpConnectionPoolRun,
7+} from "@executor/source-mcp";
68 import {
79 ExecutionInteractionIdSchema,
810 type Execution,
@@ -13,7 +15,9 @@ import * as Deferred from "effect/Deferred"; · 4 unmodified lines
1315 import * as Effect from "effect/Effect";
1416 import * as Layer from "effect/Layer";
1517
16−import type { ExecutorStateStoreShape } from "../executor-state-store";
18+import type {
19+ ExecutorStateStoreShape,
20+} from "../executor-state-store";
1721
1822 type VisibleExecutionState =
1923 | "running"
packages/platform/sdk/src/runtime/execution/mcp-resume.test.ts+65−27
@@ -1,29 +1,67 @@
1−import { randomUUID } from "node:crypto";
2−
3−import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";
4−import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5−import { Client } from "@modelcontextprotocol/sdk/client/index.js";
6−import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
7−import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
8−import { tmpdir } from "node:os";
9−import { join } from "node:path";
10−import { FileSystem } from "@effect/platform";
11−import { NodeFileSystem } from "@effect/platform-node";
12−import { describe, expect, it } from "@effect/vitest";
13−import { assertTrue } from "@effect/vitest/utils";
1+import {
2+ randomUUID,
3+} from "node:crypto";
4+
5+import {
6+ createMcpExpressApp,
7+} from "@modelcontextprotocol/sdk/server/express.js";
8+import {
9+ McpServer,
10+} from "@modelcontextprotocol/sdk/server/mcp.js";
11+import {
12+ Client,
13+} from "@modelcontextprotocol/sdk/client/index.js";
14+import {
15+ StreamableHTTPClientTransport,
16+} from "@modelcontextprotocol/sdk/client/streamableHttp.js";
17+import {
18+ StreamableHTTPServerTransport,
19+} from "@modelcontextprotocol/sdk/server/streamableHttp.js";
20+import {
21+ tmpdir,
22+} from "node:os";
23+import {
24+ join,
25+} from "node:path";
26+import {
27+ FileSystem,
28+} from "@effect/platform";
29+import {
30+ NodeFileSystem,
31+} from "@effect/platform-node";
32+import {
33+ describe,
34+ expect,
35+ it,
36+} from "@effect/vitest";
37+import {
38+ assertTrue,
39+} from "@effect/vitest/utils";
1440 import * as Effect from "effect/Effect";
1541 import * as Option from "effect/Option";
16−import { z } from "zod/v4";
42+import {
43+ z,
44+} from "zod/v4";
1745
18−import { makeToolInvokerFromTools } from "@executor/codemode-core";
19−import { discoverMcpToolsFromConnector } from "@executor/source-mcp";
20−import { makeDenoSubprocessExecutor } from "@executor/runtime-deno-subprocess";
46+import {
47+ makeToolInvokerFromTools,
48+} from "@executor/codemode-core";
49+import {
50+ discoverMcpToolsFromConnector,
51+} from "@executor/source-mcp";
52+import {
53+ makeDenoSubprocessExecutor,
54+} from "@executor/runtime-deno-subprocess";
2155
2256 import {
2357 type ResolveExecutionEnvironment,
2458 } from "../index";
25−import { createLocalExecutorRuntime as createExecutorRuntime } from "../../../../sdk-file/src/index";
26−import { withExecutorApiClient } from "./test-http-client";
59+import {
60+ createLocalExecutorRuntime as createExecutorRuntime,
61+} from "../../../../sdk-file/src/index";
62+import {
63+ withExecutorApiClient,
64+} from "./test-http-client";
2765
2866 type McpFormServer = {
2967 endpoint: string;
@@ -290,16 +328,16 @@ describe("execution-mcp-resume", () => { · 260 unmodified lines
290328 Effect.gen(function* () {
291329 const mcpServer = yield* makeFormElicitationServer;
292330 const fs = yield* FileSystem.FileSystem;
293− const workspaceRoot = yield* fs.makeTempDirectory({
331+ const scopeRoot = yield* fs.makeTempDirectory({
294332 directory: tmpdir(),
295333 prefix: "executor-execution-mcp-resume-",
296334 });
297335 const runtime = yield* Effect.acquireRelease(
298336 createExecutorRuntime({
299337 localDataDir: ":memory:",
300− workspaceRoot,
301− homeConfigPath: join(workspaceRoot, ".executor-home.jsonc"),
302− homeStateDirectory: join(workspaceRoot, ".executor-home-state"),
338+ workspaceRoot: scopeRoot,
339+ homeConfigPath: join(scopeRoot, ".executor-home.jsonc"),
340+ homeStateDirectory: join(scopeRoot, ".executor-home-state"),
303341 executionResolver: makeMcpExecutionResolver(mcpServer.endpoint),
304342 }),
305343 (runtime) => Effect.promise(() => runtime.close()).pipe(Effect.orDie),
@@ -310,12 +348,12 @@ describe("execution-mcp-resume", () => { · 4 unmodified lines
310348 const created = yield* withExecutorApiClient(
311349 {
312350 runtime,
313− accountId: installation.accountId,
351+ actorScopeId: installation.actorScopeId,
314352 },
315353 (client) =>
316354 client.executions.create({
317355 path: {
318− workspaceId: installation.workspaceId,
356+ scopeId: installation.scopeId,
319357 },
320358 payload: {
321359 code: 'return await tools.source.form.gated_echo({ value: "from-control-plane" });',
@@ -344,12 +382,12 @@ describe("execution-mcp-resume", () => { · 22 unmodified lines
344382 const resumed = yield* withExecutorApiClient(
345383 {
346384 runtime,
347− accountId: installation.accountId,
385+ actorScopeId: installation.actorScopeId,
348386 },
349387 (client) =>
350388 client.executions.resume({
351389 path: {
352− workspaceId: installation.workspaceId,
390+ scopeId: installation.scopeId,
353391 executionId: created.execution.id,
354392 },
355393 payload: {
packages/platform/sdk/src/runtime/execution/runtime-config.test.ts+36−20
@@ -1,20 +1,36 @@
1−import { tmpdir } from "node:os";
2−import { join } from "node:path";
3−import { FileSystem } from "@effect/platform";
4−import { NodeFileSystem } from "@effect/platform-node";
5−import { describe, expect, it } from "@effect/vitest";
1+import {
2+ tmpdir,
3+} from "node:os";
4+import {
5+ join,
6+} from "node:path";
7+import {
8+ FileSystem,
9+} from "@effect/platform";
10+import {
11+ NodeFileSystem,
12+} from "@effect/platform-node";
13+import {
14+ describe,
15+ expect,
16+ it,
17+} from "@effect/vitest";
618 import * as Effect from "effect/Effect";
719
8−import { createLocalExecutorRuntime as createExecutorRuntime } from "../../../../sdk-file/src/index";
9−import { withExecutorApiClient } from "./test-http-client";
20+import {
21+ createLocalExecutorRuntime as createExecutorRuntime,
22+} from "../../../../sdk-file/src/index";
23+import {
24+ withExecutorApiClient,
25+} from "./test-http-client";
1026
1127 const writeProjectConfig = (
12− workspaceRoot: string,
28+ scopeRoot: string,
1329 config: Record<string, unknown>,
1430 ) =>
1531 Effect.gen(function* () {
1632 const fs = yield* FileSystem.FileSystem;
17− const configDirectory = join(workspaceRoot, ".executor");
33+ const configDirectory = join(scopeRoot, ".executor");
1834 yield* fs.makeDirectory(configDirectory, { recursive: true });
1935 yield* fs.writeFileString(
2036 join(configDirectory, "executor.jsonc"),
@@ -26,16 +42,16 @@ const makeRuntime = (config: Record<string, unknown>) => { · 5 unmodified lines
2642 return Effect.acquireRelease(
2743 Effect.gen(function* () {
2844 const fs = yield* FileSystem.FileSystem;
29− const workspaceRoot = yield* fs.makeTempDirectory({
45+ const scopeRoot = yield* fs.makeTempDirectory({
3046 directory: tmpdir(),
3147 prefix: "executor-runtime-config-",
3248 });
33− yield* writeProjectConfig(workspaceRoot, config);
49+ yield* writeProjectConfig(scopeRoot, config);
3450
3551 return yield* createExecutorRuntime({
36− workspaceRoot,
37− homeConfigPath: join(workspaceRoot, ".executor-home.jsonc"),
38− homeStateDirectory: join(workspaceRoot, ".executor-home-state"),
52+ workspaceRoot: scopeRoot,
53+ homeConfigPath: join(scopeRoot, ".executor-home.jsonc"),
54+ homeStateDirectory: join(scopeRoot, ".executor-home-state"),
3955 });
4056 }).pipe(Effect.provide(NodeFileSystem.layer)),
4157 (runtime) => Effect.promise(() => runtime.close()).pipe(Effect.orDie),
@@ -53,12 +69,12 @@ describe("execution runtime config", () => { · 11 unmodified lines
5369 const execution = yield* withExecutorApiClient(
5470 {
5571 runtime,
56− accountId: installation.accountId,
72+ actorScopeId: installation.actorScopeId,
5773 },
5874 (client) =>
5975 client.executions.create({
6076 path: {
61− workspaceId: installation.workspaceId,
77+ scopeId: installation.scopeId,
6278 },
6379 payload: {
6480 code: 'await fetch("https://example.com"); return 1;',
@@ -85,12 +101,12 @@ describe("execution runtime config", () => { · 20 unmodified lines
85101 const execution = yield* withExecutorApiClient(
86102 {
87103 runtime,
88− accountId: installation.accountId,
104+ actorScopeId: installation.actorScopeId,
89105 },
90106 (client) =>
91107 client.executions.create({
92108 path: {
93− workspaceId: installation.workspaceId,
109+ scopeId: installation.scopeId,
94110 },
95111 payload: {
96112 code: 'await fetch("https://example.com"); return 1;',
@@ -117,12 +133,12 @@ describe("execution runtime config", () => { · 20 unmodified lines
117133 const execution = yield* withExecutorApiClient(
118134 {
119135 runtime,
120− accountId: installation.accountId,
136+ actorScopeId: installation.actorScopeId,
121137 },
122138 (client) =>
123139 client.executions.create({
124140 path: {
125− workspaceId: installation.workspaceId,
141+ scopeId: installation.scopeId,
126142 },
127143 payload: {
128144 code: 'return typeof Deno !== "undefined";',
packages/platform/sdk/src/runtime/execution/runtime.ts+16−5
@@ -1,8 +1,19 @@
1−import type { CodeExecutor } from "@executor/codemode-core";
2−import { makeDenoSubprocessExecutor } from "@executor/runtime-deno-subprocess";
3−import { makeQuickJsExecutor } from "@executor/runtime-quickjs";
4−import { makeSesExecutor } from "@executor/runtime-ses";
5−import type { LocalExecutorConfig, LocalExecutorRuntime } from "#schema";
1+import type {
2+ CodeExecutor,
3+} from "@executor/codemode-core";
4+import {
5+ makeDenoSubprocessExecutor,
6+} from "@executor/runtime-deno-subprocess";
7+import {
8+ makeQuickJsExecutor,
9+} from "@executor/runtime-quickjs";
10+import {
11+ makeSesExecutor,
12+} from "@executor/runtime-ses";
13+import type {
14+ LocalExecutorConfig,
15+ LocalExecutorRuntime,
16+} from "#schema";
617
718 const DEFAULT_EXECUTION_RUNTIME: LocalExecutorRuntime = "quickjs";
819
packages/platform/sdk/src/runtime/execution/scope/authorization.tsrenamed from packages/platform/sdk/src/runtime/execution/workspace/authorization.ts+33−16
@@ -1,15 +1,32 @@
1−import { type ToolPath, makeToolInvokerFromTools } from "@executor/codemode-core";
2−import type { Source } from "#schema";
1+import {
2+ type ToolPath,
3+ makeToolInvokerFromTools,
4+} from "@executor/codemode-core";
5+import type {
6+ Source,
7+} from "#schema";
38 import * as Either from "effect/Either";
49 import * as Effect from "effect/Effect";
510 import * as Schema from "effect/Schema";
611
7−import type { LoadedSourceCatalogToolIndexEntry } from "../../catalog/source/runtime";
8−import type { SecretMaterialResolveContext } from "../../workspace/secret-material-providers";
9−import type { WorkspaceStorageServices } from "../../workspace/storage";
10−import { invocationDescriptorFromTool } from "../ir-execution";
11−import { evaluateInvocationPolicy } from "../../policy/invocation-policy-engine";
12−import { loadRuntimeLocalWorkspacePolicies } from "../../../policies/operations";
12+import type {
13+ LoadedSourceCatalogToolIndexEntry,
14+} from "../../catalog/source/runtime";
15+import type {
16+ SecretMaterialResolveContext,
17+} from "../../scope/secret-material-providers";
18+import type {
19+ ScopeStorageServices,
20+} from "../../scope/storage";
21+import {
22+ invocationDescriptorFromTool,
23+} from "../ir-execution";
24+import {
25+ evaluateInvocationPolicy,
26+} from "../../policy/invocation-policy-engine";
27+import {
28+ loadRuntimeLocalScopePolicies,
29+} from "../../../policies/operations";
1330
1431 const asToolPath = (value: string): ToolPath => value as ToolPath;
1532
@@ -64,23 +81,23 @@ export const toSecretResolutionContext = ( · 48 unmodified lines
6481 const failAuthorization = (message: string): Effect.Effect<never, Error, never> =>
6582 Effect.fail(new Error(message));
6683
67−type WorkspaceToolElicitation = Parameters<
84+type ScopeToolElicitation = Parameters<
6885 typeof makeToolInvokerFromTools
6986 >[0]["onElicitation"];
7087
7188 export const authorizePersistedToolInvocation = (input: {
72− workspaceId: Source["workspaceId"];
89+ scopeId: Source["scopeId"];
7390 tool: LoadedSourceCatalogToolIndexEntry;
7491 args: unknown;
7592 context?: Record<string, unknown>;
76− onElicitation?: WorkspaceToolElicitation;
77−}): Effect.Effect<void, Error, WorkspaceStorageServices> =>
93+ onElicitation?: ScopeToolElicitation;
94+}): Effect.Effect<void, Error, ScopeStorageServices> =>
7895 Effect.gen(function* () {
7996 const descriptor = invocationDescriptorFromTool({
8097 tool: input.tool,
8198 });
82− const localWorkspacePolicies = yield* loadRuntimeLocalWorkspacePolicies(
83− input.workspaceId,
99+ const localScopePolicies = yield* loadRuntimeLocalScopePolicies(
100+ input.scopeId,
84101 ).pipe(
85102 Effect.mapError((cause) =>
86103 cause instanceof Error ? cause : new Error(String(cause)),
@@ -90,9 +107,9 @@ export const authorizePersistedToolInvocation = (input: { · 3 unmodified lines
90107 const decision = evaluateInvocationPolicy({
91108 descriptor,
92109 args: input.args,
93− policies: localWorkspacePolicies.policies,
110+ policies: localScopePolicies.policies,
94111 context: {
95− workspaceId: input.workspaceId,
112+ scopeId: input.scopeId,
96113 },
97114 });
98115
packages/platform/sdk/src/runtime/execution/scope/environment.tsrenamed from packages/platform/sdk/src/runtime/execution/workspace/environment.ts+55−37
@@ -14,18 +14,30 @@ import { · 13 unmodified lines
1414 createCodeExecutorForRuntime,
1515 resolveConfiguredExecutionRuntime,
1616 } from "../runtime";
17−import { createWorkspaceToolInvoker } from "./tool-invoker";
18−import { RuntimeSourceAuthServiceTag } from "../../sources/source-auth-service";
19−import { RuntimeSourceCatalogStoreService } from "../../catalog/source/runtime";
20−import { RuntimeSourceAuthMaterialService } from "../../auth/source-auth-material";
21−import { RuntimeSourceCatalogSyncService } from "../../catalog/source/sync";
22−import { getRuntimeLocalWorkspaceOption } from "../../workspace/runtime-context";
17+import {
18+ createScopeToolInvoker,
19+} from "./tool-invoker";
20+import {
21+ RuntimeSourceAuthServiceTag,
22+} from "../../sources/source-auth-service";
23+import {
24+ RuntimeSourceCatalogStoreService,
25+} from "../../catalog/source/runtime";
26+import {
27+ RuntimeSourceAuthMaterialService,
28+} from "../../auth/source-auth-material";
29+import {
30+ RuntimeSourceCatalogSyncService,
31+} from "../../catalog/source/sync";
32+import {
33+ getRuntimeLocalScopeOption,
34+} from "../../scope/runtime-context";
2335 import {
2436 LocalInstanceConfigService,
2537 SecretMaterialDeleterService,
2638 SecretMaterialStorerService,
2739 SecretMaterialUpdaterService,
28−} from "../../workspace/secret-material-providers";
40+} from "../../scope/secret-material-providers";
2941 import {
3042 LocalToolRuntimeLoaderService,
3143 type LocalToolRuntimeLoaderShape,
@@ -36,14 +48,20 @@ import { · 4 unmodified lines
3648 type InstallationStoreShape,
3749 SourceArtifactStore,
3850 type SourceArtifactStoreShape,
39− WorkspaceConfigStore,
40− type WorkspaceConfigStoreShape,
41− WorkspaceStateStore,
42− type WorkspaceStateStoreShape,
43−} from "../../workspace/storage";
44−import { ExecutorStateStore } from "../../executor-state-store";
45−import { RuntimeSourceStoreService } from "../../sources/source-store";
46−import type { CreateWorkspaceInternalToolMap } from "./tool-invoker";
51+ ScopeConfigStore,
52+ type ScopeConfigStoreShape,
53+ ScopeStateStore,
54+ type ScopeStateStoreShape,
55+} from "../../scope/storage";
56+import {
57+ ExecutorStateStore,
58+} from "../../executor-state-store";
59+import {
60+ RuntimeSourceStoreService,
61+} from "../../sources/source-store";
62+import type {
63+ CreateScopeInternalToolMap,
64+} from "./tool-invoker";
4765 export {
4866 createCodeExecutorForRuntime,
4967 resolveConfiguredExecutionRuntime,
@@ -56,7 +74,7 @@ const createEmptyLocalToolRuntime = (): LocalToolRuntime => ({ · 6 unmodified lines
5674 toolPaths: new Set<string>(),
5775 });
5876
59−export const createWorkspaceExecutionEnvironmentResolver =
77+export const createScopeExecutionEnvironmentResolver =
6078 (input: {
6179 executorStateStore: Effect.Effect.Success<typeof ExecutorStateStore>;
6280 sourceStore: Effect.Effect.Success<typeof RuntimeSourceStoreService>;
@@ -78,25 +96,25 @@ export const createWorkspaceExecutionEnvironmentResolver = · 15 unmodified lines
7896 storeSecretMaterial: Effect.Effect.Success<typeof SecretMaterialStorerService>;
7997 deleteSecretMaterial: Effect.Effect.Success<typeof SecretMaterialDeleterService>;
8098 updateSecretMaterial: Effect.Effect.Success<typeof SecretMaterialUpdaterService>;
81− workspaceConfigStore: WorkspaceConfigStoreShape;
82− workspaceStateStore: WorkspaceStateStoreShape;
99+ scopeConfigStore: ScopeConfigStoreShape;
100+ scopeStateStore: ScopeStateStoreShape;
83101 sourceArtifactStore: SourceArtifactStoreShape;
84− createInternalToolMap?: CreateWorkspaceInternalToolMap;
102+ createInternalToolMap?: CreateScopeInternalToolMap;
85103 }): ResolveExecutionEnvironment =>
86− ({ workspaceId, accountId, onElicitation }) =>
104+ ({ scopeId, actorScopeId, onElicitation }) =>
87105 Effect.gen(function* () {
88− const runtimeLocalWorkspace = yield* getRuntimeLocalWorkspaceOption();
106+ const runtimeLocalScope = yield* getRuntimeLocalScopeOption();
89107 const loadedConfig =
90− runtimeLocalWorkspace === null
108+ runtimeLocalScope === null
91109 ? null
92− : yield* input.workspaceConfigStore.load();
110+ : yield* input.scopeConfigStore.load();
93111 const localToolRuntime =
94− runtimeLocalWorkspace === null
112+ runtimeLocalScope === null
95113 ? createEmptyLocalToolRuntime()
96114 : yield* input.localToolRuntimeLoader.load();
97− const { catalog, toolInvoker } = createWorkspaceToolInvoker({
98− workspaceId,
99− accountId,
115+ const { catalog, toolInvoker } = createScopeToolInvoker({
116+ scopeId,
117+ actorScopeId,
100118 executorStateStore: input.executorStateStore,
101119 sourceStore: input.sourceStore,
102120 sourceCatalogSyncService: input.sourceCatalogSyncService,
@@ -106,12 +124,12 @@ export const createWorkspaceExecutionEnvironmentResolver = · 3 unmodified lines
106124 storeSecretMaterial: input.storeSecretMaterial,
107125 deleteSecretMaterial: input.deleteSecretMaterial,
108126 updateSecretMaterial: input.updateSecretMaterial,
109− workspaceConfigStore: input.workspaceConfigStore,
110− workspaceStateStore: input.workspaceStateStore,
127+ scopeConfigStore: input.scopeConfigStore,
128+ scopeStateStore: input.scopeStateStore,
111129 sourceArtifactStore: input.sourceArtifactStore,
112130 sourceAuthMaterialService: input.sourceAuthMaterialService,
113131 sourceAuthService: input.sourceAuthService,
114− runtimeLocalWorkspace,
132+ runtimeLocalScope,
115133 localToolRuntime,
116134 createInternalToolMap: input.createInternalToolMap,
117135 onElicitation,
@@ -132,13 +150,13 @@ export class RuntimeExecutionResolverService extends Context.Tag( · 14 unmodified lines
132150 "#runtime/RuntimeExecutionResolverService",
133151 )<
134152 RuntimeExecutionResolverService,
135− ReturnType<typeof createWorkspaceExecutionEnvironmentResolver>
153+ ReturnType<typeof createScopeExecutionEnvironmentResolver>
136154 >() {}
137155
138156 export const RuntimeExecutionResolverLive = (
139157 input: {
140158 executionResolver?: ResolveExecutionEnvironment;
141− createInternalToolMap?: CreateWorkspaceInternalToolMap;
159+ createInternalToolMap?: CreateScopeInternalToolMap;
142160 } = {},
143161 ) =>
144162 input.executionResolver
@@ -160,11 +178,11 @@ export const RuntimeExecutionResolverLive = ( · 15 unmodified lines
160178 const storeSecretMaterial = yield* SecretMaterialStorerService;
161179 const deleteSecretMaterial = yield* SecretMaterialDeleterService;
162180 const updateSecretMaterial = yield* SecretMaterialUpdaterService;
163− const workspaceConfigStore = yield* WorkspaceConfigStore;
164− const workspaceStateStore = yield* WorkspaceStateStore;
181+ const scopeConfigStore = yield* ScopeConfigStore;
182+ const scopeStateStore = yield* ScopeStateStore;
165183 const sourceArtifactStore = yield* SourceArtifactStore;
166184
167− return createWorkspaceExecutionEnvironmentResolver({
185+ return createScopeExecutionEnvironmentResolver({
168186 executorStateStore,
169187 sourceStore,
170188 sourceCatalogSyncService,
@@ -177,8 +195,8 @@ export const RuntimeExecutionResolverLive = ( · 6 unmodified lines
177195 storeSecretMaterial,
178196 deleteSecretMaterial,
179197 updateSecretMaterial,
180− workspaceConfigStore,
181− workspaceStateStore,
198+ scopeConfigStore,
199+ scopeStateStore,
182200 sourceArtifactStore,
183201 createInternalToolMap: input.createInternalToolMap,
184202 });
packages/platform/sdk/src/runtime/execution/scope/local.ts+12−0
@@ -0,0 +1,12 @@
1+import * as Effect from "effect/Effect";
2+
3+import {
4+ provideOptionalRuntimeLocalScope,
5+ type RuntimeLocalScopeState,
6+} from "../../scope/runtime-context";
7+
8+export const provideRuntimeLocalScope = <A, E, R>(
9+ effect: Effect.Effect<A, E, R>,
10+ runtimeLocalScope: RuntimeLocalScopeState | null,
11+): Effect.Effect<A, E, R> =>
12+ provideOptionalRuntimeLocalScope(effect, runtimeLocalScope);
packages/platform/sdk/src/runtime/execution/scope/source-catalog.tsrenamed from packages/platform/sdk/src/runtime/execution/workspace/source-catalog.ts+45−38
@@ -2,7 +2,10 @@ import { · 1 unmodified line
22 createToolCatalogFromEntries,
33 type ToolCatalog,
44 } from "@executor/codemode-core";
5−import type { AccountId, Source } from "#schema";
5+import type {
6+ ScopeId,
7+ Source,
8+} from "#schema";
69 import * as Effect from "effect/Effect";
710
811 import {
@@ -10,15 +13,19 @@ import { · 1 unmodified line
1013 type LoadedSourceCatalogToolIndexEntry,
1114 catalogToolCatalogEntry,
1215 } from "../../catalog/source/runtime";
13−import type { RuntimeLocalWorkspaceState } from "../../workspace/runtime-context";
16+import type {
17+ RuntimeLocalScopeState,
18+} from "../../scope/runtime-context";
1419 import {
15− makeWorkspaceStorageLayer,
20+ makeScopeStorageLayer,
1621 type SourceArtifactStoreShape,
17− type WorkspaceConfigStoreShape,
18− type WorkspaceStateStoreShape,
19− type WorkspaceStorageServices,
20−} from "../../workspace/storage";
21−import { provideRuntimeLocalWorkspace } from "./local";
22+ type ScopeConfigStoreShape,
23+ type ScopeStateStoreShape,
24+ type ScopeStorageServices,
25+} from "../../scope/storage";
26+import {
27+ provideRuntimeLocalScope,
28+} from "./local";
2229
2330 const tokenize = (value: string): string[] =>
2431 value
@@ -74,19 +81,19 @@ const queryTokenWeight = (token: string): number => · 49 unmodified lines
7481 LOW_SIGNAL_QUERY_TOKENS.has(token) ? 0.25 : 1;
7582
7683 export const loadWorkspaceCatalogTools = (input: {
77− workspaceId: Source["workspaceId"];
78− accountId: AccountId;
84+ scopeId: Source["scopeId"];
85+ actorScopeId: ScopeId;
7986 sourceCatalogStore: Effect.Effect.Success<typeof RuntimeSourceCatalogStoreService>;
8087 includeSchemas: boolean;
8188 }): Effect.Effect<
8289 readonly LoadedSourceCatalogToolIndexEntry[],
8390 Error,
84− WorkspaceStorageServices
91+ ScopeStorageServices
8592 > =>
8693 Effect.map(
8794 input.sourceCatalogStore.loadWorkspaceSourceCatalogToolIndex({
88− workspaceId: input.workspaceId,
89− actorAccountId: input.accountId,
95+ scopeId: input.scopeId,
96+ actorScopeId: input.actorScopeId,
9097 includeSchemas: input.includeSchemas,
9198 }),
9299 (tools) =>
@@ -96,20 +103,20 @@ export const loadWorkspaceCatalogTools = (input: { · 3 unmodified lines
96103 );
97104
98105 export const loadWorkspaceCatalogToolByPath = (input: {
99− workspaceId: Source["workspaceId"];
100− accountId: AccountId;
106+ scopeId: Source["scopeId"];
107+ actorScopeId: ScopeId;
101108 sourceCatalogStore: Effect.Effect.Success<typeof RuntimeSourceCatalogStoreService>;
102109 path: string;
103110 includeSchemas: boolean;
104111 }): Effect.Effect<
105112 LoadedSourceCatalogToolIndexEntry | null,
106113 Error,
107− WorkspaceStorageServices
114+ ScopeStorageServices
108115 > =>
109116 input.sourceCatalogStore.loadWorkspaceSourceCatalogToolByPath({
110− workspaceId: input.workspaceId,
117+ scopeId: input.scopeId,
111118 path: input.path,
112− actorAccountId: input.accountId,
119+ actorScopeId: input.actorScopeId,
113120 includeSchemas: input.includeSchemas,
114121 }).pipe(
115122 Effect.map((tool) =>
@@ -245,28 +252,28 @@ const scoreCatalogTool = ( · 129 unmodified lines
245252 return score;
246253 };
247254
248−export const createWorkspaceSourceCatalog = (input: {
249− workspaceId: Source["workspaceId"];
250− accountId: AccountId;
255+export const createScopeSourceCatalog = (input: {
256+ scopeId: Source["scopeId"];
257+ actorScopeId: ScopeId;
251258 sourceCatalogStore: Effect.Effect.Success<typeof RuntimeSourceCatalogStoreService>;
252− workspaceConfigStore: WorkspaceConfigStoreShape;
253− workspaceStateStore: WorkspaceStateStoreShape;
259+ scopeConfigStore: ScopeConfigStoreShape;
260+ scopeStateStore: ScopeStateStoreShape;
254261 sourceArtifactStore: SourceArtifactStoreShape;
255− runtimeLocalWorkspace: RuntimeLocalWorkspaceState | null;
262+ runtimeLocalScope: RuntimeLocalScopeState | null;
256263 }): ToolCatalog => {
257− const workspaceStorageLayer = makeWorkspaceStorageLayer({
258− workspaceConfigStore: input.workspaceConfigStore,
259− workspaceStateStore: input.workspaceStateStore,
264+ const scopeStorageLayer = makeScopeStorageLayer({
265+ scopeConfigStore: input.scopeConfigStore,
266+ scopeStateStore: input.scopeStateStore,
260267 sourceArtifactStore: input.sourceArtifactStore,
261268 });
262269 const provideWorkspaceStorage = <A, E, R>(effect: Effect.Effect<A, E, R>) =>
263− effect.pipe(Effect.provide(workspaceStorageLayer));
270+ effect.pipe(Effect.provide(scopeStorageLayer));
264271
265272 const createSharedCatalog = (includeSchemas: boolean): Effect.Effect<ToolCatalog, Error, never> =>
266273 provideWorkspaceStorage(Effect.gen(function* () {
267274 const catalogTools = yield* loadWorkspaceCatalogTools({
268− workspaceId: input.workspaceId,
269− accountId: input.accountId,
275+ scopeId: input.scopeId,
276+ actorScopeId: input.actorScopeId,
270277 sourceCatalogStore: input.sourceCatalogStore,
271278 includeSchemas,
272279 });
@@ -283,15 +290,15 @@ export const createWorkspaceSourceCatalog = (input: { · 10 unmodified lines
283290
284291 return {
285292 listNamespaces: ({ limit }) =>
286− provideRuntimeLocalWorkspace(
293+ provideRuntimeLocalScope(
287294 Effect.flatMap(createSharedCatalog(false), (catalog) =>
288295 catalog.listNamespaces({ limit }),
289296 ),
290− input.runtimeLocalWorkspace,
297+ input.runtimeLocalScope,
291298 ),
292299
293300 listTools: ({ namespace, query, limit, includeSchemas = false }) =>
294− provideRuntimeLocalWorkspace(
301+ provideRuntimeLocalScope(
295302 Effect.flatMap(createSharedCatalog(includeSchemas), (catalog) =>
296303 catalog.listTools({
297304 ...(namespace !== undefined ? { namespace } : {}),
@@ -300,19 +307,19 @@ export const createWorkspaceSourceCatalog = (input: { · 2 unmodified lines
300307 includeSchemas,
301308 }),
302309 ),
303− input.runtimeLocalWorkspace,
310+ input.runtimeLocalScope,
304311 ),
305312
306313 getToolByPath: ({ path, includeSchemas }) =>
307− provideRuntimeLocalWorkspace(
314+ provideRuntimeLocalScope(
308315 Effect.flatMap(createSharedCatalog(includeSchemas), (catalog) =>
309316 catalog.getToolByPath({ path, includeSchemas }),
310317 ),
311− input.runtimeLocalWorkspace,
318+ input.runtimeLocalScope,
312319 ),
313320
314321 searchTools: ({ query, namespace, limit }) =>
315− provideRuntimeLocalWorkspace(
322+ provideRuntimeLocalScope(
316323 Effect.flatMap(createSharedCatalog(false), (catalog) =>
317324 catalog.searchTools({
318325 query,
@@ -320,7 +327,7 @@ export const createWorkspaceSourceCatalog = (input: { · 1 unmodified line
320327 limit,
321328 }),
322329 ),
323− input.runtimeLocalWorkspace,
330+ input.runtimeLocalScope,
324331 ),
325332 } satisfies ToolCatalog;
326333 };
packages/platform/sdk/src/runtime/execution/scope/tool-invoker.tsrenamed from packages/platform/sdk/src/runtime/execution/workspace/tool-invoker.ts+92−65
@@ -8,46 +8,73 @@ import { · 7 unmodified lines
88 type ToolMap,
99 type ToolInvoker,
1010 } from "@executor/codemode-core";
11−import type { AccountId, Source } from "#schema";
11+import type {
12+ ScopeId,
13+ Source,
14+} from "#schema";
1215 import * as Effect from "effect/Effect";
1316
14−import { RuntimeSourceAuthMaterialService } from "../../auth/source-auth-material";
15−import { RuntimeSourceCatalogStoreService } from "../../catalog/source/runtime";
16−import type { RuntimeLocalWorkspaceState } from "../../workspace/runtime-context";
17−import { type LocalToolRuntime } from "../../local-tool-runtime";
17+import {
18+ RuntimeSourceAuthMaterialService,
19+} from "../../auth/source-auth-material";
20+import {
21+ RuntimeSourceCatalogStoreService,
22+} from "../../catalog/source/runtime";
23+import type {
24+ RuntimeLocalScopeState,
25+} from "../../scope/runtime-context";
26+import {
27+ type LocalToolRuntime,
28+} from "../../local-tool-runtime";
1829 import {
1930 type InstallationStoreShape,
20− makeWorkspaceStorageLayer,
31+ makeScopeStorageLayer,
2132 type SourceArtifactStoreShape,
22− type WorkspaceConfigStoreShape,
23− type WorkspaceStateStoreShape,
24−} from "../../workspace/storage";
33+ type ScopeConfigStoreShape,
34+ type ScopeStateStoreShape,
35+} from "../../scope/storage";
2536 import type {
2637 DeleteSecretMaterial,
2738 ResolveInstanceConfig,
2839 StoreSecretMaterial,
2940 UpdateSecretMaterial,
30−} from "../../workspace/secret-material-providers";
31−import type { ExecutorStateStoreShape } from "../../executor-state-store";
32−import { RuntimeSourceAuthService } from "../../sources/source-auth-service";
33−import { type RuntimeSourceStore } from "../../sources/source-store";
34−import { createExecutorToolMap } from "../../sources/executor-tools";
35−import { RuntimeSourceCatalogSyncService } from "../../catalog/source/sync";
36−import { invokeIrTool } from "../ir-execution";
41+} from "../../scope/secret-material-providers";
42+import type {
43+ ExecutorStateStoreShape,
44+} from "../../executor-state-store";
45+import {
46+ RuntimeSourceAuthService,
47+} from "../../sources/source-auth-service";
48+import {
49+ type RuntimeSourceStore,
50+} from "../../sources/source-store";
51+import {
52+ createExecutorToolMap,
53+} from "../../sources/executor-tools";
54+import {
55+ RuntimeSourceCatalogSyncService,
56+} from "../../catalog/source/sync";
57+import {
58+ invokeIrTool,
59+} from "../ir-execution";
3760 import {
3861 authorizePersistedToolInvocation,
3962 toSecretResolutionContext,
4063 } from "./authorization";
41−import { provideRuntimeLocalWorkspace } from "./local";
4264 import {
43− createWorkspaceSourceCatalog,
65+ provideRuntimeLocalScope,
66+} from "./local";
67+import {
68+ createScopeSourceCatalog,
4469 loadWorkspaceCatalogToolByPath,
4570 } from "./source-catalog";
46−import { runtimeEffectError } from "../../effect-errors";
71+import {
72+ runtimeEffectError,
73+} from "../../effect-errors";
4774
48−export type WorkspaceInternalToolContext = {
49− workspaceId: Source["workspaceId"];
50− accountId: AccountId;
75+export type ScopeInternalToolContext = {
76+ scopeId: Source["scopeId"];
77+ actorScopeId: ScopeId;
5178 executorStateStore: ExecutorStateStoreShape;
5279 sourceStore: RuntimeSourceStore;
5380 sourceCatalogSyncService: Effect.Effect.Success<
@@ -59,19 +86,19 @@ export type WorkspaceInternalToolContext = { · 5 unmodified lines
5986 storeSecretMaterial: StoreSecretMaterial;
6087 deleteSecretMaterial: DeleteSecretMaterial;
6188 updateSecretMaterial: UpdateSecretMaterial;
62− workspaceConfigStore: WorkspaceConfigStoreShape;
63− workspaceStateStore: WorkspaceStateStoreShape;
89+ scopeConfigStore: ScopeConfigStoreShape;
90+ scopeStateStore: ScopeStateStoreShape;
6491 sourceArtifactStore: SourceArtifactStoreShape;
65− runtimeLocalWorkspace: RuntimeLocalWorkspaceState | null;
92+ runtimeLocalScope: RuntimeLocalScopeState | null;
6693 };
6794
68−export type CreateWorkspaceInternalToolMap = (
69− input: WorkspaceInternalToolContext,
95+export type CreateScopeInternalToolMap = (
96+ input: ScopeInternalToolContext,
7097 ) => ToolMap;
7198
72−export const createWorkspaceToolInvoker = (input: {
73− workspaceId: Source["workspaceId"];
74− accountId: AccountId;
99+export const createScopeToolInvoker = (input: {
100+ scopeId: Source["scopeId"];
101+ actorScopeId: ScopeId;
75102 executorStateStore: ExecutorStateStoreShape;
76103 sourceStore: RuntimeSourceStore;
77104 sourceCatalogSyncService: Effect.Effect.Success<
@@ -85,16 +112,16 @@ export const createWorkspaceToolInvoker = (input: { · 7 unmodified lines
85112 storeSecretMaterial: StoreSecretMaterial;
86113 deleteSecretMaterial: DeleteSecretMaterial;
87114 updateSecretMaterial: UpdateSecretMaterial;
88− workspaceConfigStore: WorkspaceConfigStoreShape;
89− workspaceStateStore: WorkspaceStateStoreShape;
115+ scopeConfigStore: ScopeConfigStoreShape;
116+ scopeStateStore: ScopeStateStoreShape;
90117 sourceArtifactStore: SourceArtifactStoreShape;
91118 sourceAuthMaterialService: Effect.Effect.Success<
92119 typeof RuntimeSourceAuthMaterialService
93120 >;
94121 sourceAuthService: RuntimeSourceAuthService;
95− runtimeLocalWorkspace: RuntimeLocalWorkspaceState | null;
122+ runtimeLocalScope: RuntimeLocalScopeState | null;
96123 localToolRuntime: LocalToolRuntime;
97− createInternalToolMap?: CreateWorkspaceInternalToolMap;
124+ createInternalToolMap?: CreateScopeInternalToolMap;
98125 onElicitation?: Parameters<
99126 typeof makeToolInvokerFromTools
100127 >[0]["onElicitation"];
@@ -102,28 +129,28 @@ export const createWorkspaceToolInvoker = (input: { · 1 unmodified line
102129 catalog: ToolCatalog;
103130 toolInvoker: ToolInvoker;
104131 } => {
105− const workspaceStorageLayer = makeWorkspaceStorageLayer({
106− workspaceConfigStore: input.workspaceConfigStore,
107− workspaceStateStore: input.workspaceStateStore,
132+ const scopeStorageLayer = makeScopeStorageLayer({
133+ scopeConfigStore: input.scopeConfigStore,
134+ scopeStateStore: input.scopeStateStore,
108135 sourceArtifactStore: input.sourceArtifactStore,
109136 });
110137 const provideWorkspaceStorage = <A, E, R>(effect: Effect.Effect<A, E, R>) =>
111− effect.pipe(Effect.provide(workspaceStorageLayer));
138+ effect.pipe(Effect.provide(scopeStorageLayer));
112139
113140 const executorTools = createExecutorToolMap({
114− workspaceId: input.workspaceId,
115− accountId: input.accountId,
141+ scopeId: input.scopeId,
142+ actorScopeId: input.actorScopeId,
116143 sourceAuthService: input.sourceAuthService,
117144 installationStore: input.installationStore,
118− workspaceConfigStore: input.workspaceConfigStore,
119− workspaceStateStore: input.workspaceStateStore,
145+ scopeConfigStore: input.scopeConfigStore,
146+ scopeStateStore: input.scopeStateStore,
120147 sourceArtifactStore: input.sourceArtifactStore,
121− runtimeLocalWorkspace: input.runtimeLocalWorkspace,
148+ runtimeLocalScope: input.runtimeLocalScope,
122149 });
123150 const internalTools =
124151 input.createInternalToolMap?.({
125− workspaceId: input.workspaceId,
126− accountId: input.accountId,
152+ scopeId: input.scopeId,
153+ actorScopeId: input.actorScopeId,
127154 executorStateStore: input.executorStateStore,
128155 sourceStore: input.sourceStore,
129156 sourceCatalogSyncService: input.sourceCatalogSyncService,
@@ -133,19 +160,19 @@ export const createWorkspaceToolInvoker = (input: { · 3 unmodified lines
133160 storeSecretMaterial: input.storeSecretMaterial,
134161 deleteSecretMaterial: input.deleteSecretMaterial,
135162 updateSecretMaterial: input.updateSecretMaterial,
136− workspaceConfigStore: input.workspaceConfigStore,
137− workspaceStateStore: input.workspaceStateStore,
163+ scopeConfigStore: input.scopeConfigStore,
164+ scopeStateStore: input.scopeStateStore,
138165 sourceArtifactStore: input.sourceArtifactStore,
139− runtimeLocalWorkspace: input.runtimeLocalWorkspace,
166+ runtimeLocalScope: input.runtimeLocalScope,
140167 }) ?? {};
141− const sourceCatalog = createWorkspaceSourceCatalog({
142− workspaceId: input.workspaceId,
143− accountId: input.accountId,
168+ const sourceCatalog = createScopeSourceCatalog({
169+ scopeId: input.scopeId,
170+ actorScopeId: input.actorScopeId,
144171 sourceCatalogStore: input.sourceCatalogStore,
145− workspaceConfigStore: input.workspaceConfigStore,
146− workspaceStateStore: input.workspaceStateStore,
172+ scopeConfigStore: input.scopeConfigStore,
173+ scopeStateStore: input.scopeStateStore,
147174 sourceArtifactStore: input.sourceArtifactStore,
148− runtimeLocalWorkspace: input.runtimeLocalWorkspace,
175+ runtimeLocalScope: input.runtimeLocalScope,
149176 });
150177 let catalog: ToolCatalog | null = null;
151178 const systemTools = createSystemToolMap({
@@ -180,25 +207,25 @@ export const createWorkspaceToolInvoker = (input: { · 28 unmodified lines
180207 args: unknown;
181208 context?: Record<string, unknown>;
182209 }) =>
183− provideRuntimeLocalWorkspace(
210+ provideRuntimeLocalScope(
184211 provideWorkspaceStorage(
185212 Effect.gen(function* () {
186213 const catalogTool = yield* loadWorkspaceCatalogToolByPath({
187− workspaceId: input.workspaceId,
188− accountId: input.accountId,
214+ scopeId: input.scopeId,
215+ actorScopeId: input.actorScopeId,
189216 sourceCatalogStore: input.sourceCatalogStore,
190217 path: invocation.path,
191218 includeSchemas: false,
192219 });
193220 if (!catalogTool) {
194221 return yield* runtimeEffectError(
195− "execution/workspace/tool-invoker",
222+ "execution/scope/tool-invoker",
196223 `Unknown tool path: ${invocation.path}`,
197224 );
198225 }
199226
200227 yield* authorizePersistedToolInvocation({
201− workspaceId: input.workspaceId,
228+ scopeId: input.scopeId,
202229 tool: catalogTool,
203230 args: invocation.args,
204231 context: invocation.context,
@@ -207,12 +234,12 @@ export const createWorkspaceToolInvoker = (input: { · 2 unmodified lines
207234
208235 const auth = yield* input.sourceAuthMaterialService.resolve({
209236 source: catalogTool.source,
210− actorAccountId: input.accountId,
237+ actorScopeId: input.actorScopeId,
211238 context: toSecretResolutionContext(invocation.context),
212239 });
213240 return yield* invokeIrTool({
214− workspaceId: input.workspaceId,
215− accountId: input.accountId,
241+ scopeId: input.scopeId,
242+ actorScopeId: input.actorScopeId,
216243 tool: catalogTool,
217244 auth,
218245 args: invocation.args,
@@ -221,18 +248,18 @@ export const createWorkspaceToolInvoker = (input: { · 2 unmodified lines
221248 });
222249 }),
223250 ),
224− input.runtimeLocalWorkspace,
251+ input.runtimeLocalScope,
225252 );
226253
227254 return {
228255 catalog,
229256 toolInvoker: {
230257 invoke: ({ path, args, context }) =>
231− provideRuntimeLocalWorkspace(
258+ provideRuntimeLocalScope(
232259 authoredToolPaths.has(path)
233260 ? authoredInvoker.invoke({ path, args, context })
234261 : invokePersistedTool({ path, args, context }),
235− input.runtimeLocalWorkspace,
262+ input.runtimeLocalScope,
236263 ),
237264 },
238265 };
packages/platform/sdk/src/runtime/execution/service.ts+35−30
@@ -16,12 +16,11 @@ import { · 15 unmodified lines
1616 ExecutionIdSchema,
1717 ExecutionInteractionIdSchema,
1818 ExecutionStepIdSchema,
19− type AccountId,
19+ type ScopeId,
2020 type Execution,
2121 type ExecutionEnvelope,
2222 type ExecutionId,
2323 type ExecutionInteraction,
24− type WorkspaceId,
2524 } from "#schema";
2625 import * as Data from "effect/Data";
2726 import * as Deferred from "effect/Deferred";
@@ -29,16 +28,18 @@ import * as Effect from "effect/Effect"; · 1 unmodified line
2928 import * as Option from "effect/Option";
3029 import * as Schema from "effect/Schema";
3130
32−import { type ResolveExecutionEnvironment } from "./state";
31+import {
32+ type ResolveExecutionEnvironment,
33+} from "./state";
3334 import {
3435 LiveExecutionManagerService,
3536 sanitizePersistedElicitationResponse,
3637 type LiveExecutionManager,
3738 } from "./live";
3839 import {
39− getRuntimeLocalWorkspaceOption,
40− provideOptionalRuntimeLocalWorkspace,
41−} from "../workspace/runtime-context";
40+ getRuntimeLocalScopeOption,
41+ provideOptionalRuntimeLocalScope,
42+} from "../scope/runtime-context";
4243 import {
4344 asOperationErrors,
4445 operationErrors,
@@ -48,8 +49,12 @@ import { · 3 unmodified lines
4849 ExecutorStateStore,
4950 type ExecutorStateStoreShape,
5051 } from "../executor-state-store";
51−import { RuntimeExecutionResolverService } from "./workspace/environment";
52−import { runtimeEffectError } from "../effect-errors";
52+import {
53+ RuntimeExecutionResolverService,
54+} from "./scope/environment";
55+import {
56+ runtimeEffectError,
57+} from "../effect-errors";
5358
5459 const executionOps = {
5560 create: operationErrors("executions.create"),
@@ -246,7 +251,7 @@ const verifyStoredStepMatches = (input: { · 190 unmodified lines
246251 const fetchExecution = (
247252 store: ExecutorStateStoreShape,
248253 input: {
249− workspaceId: Execution["workspaceId"];
254+ scopeId: Execution["scopeId"];
250255 executionId: Execution["id"];
251256 operation: OperationErrorsLike;
252257 },
@@ -254,13 +259,13 @@ const fetchExecution = ( · 1 unmodified line
254259 Effect.gen(function* () {
255260 const errors = asOperationErrors(input.operation);
256261 const existing = yield* errors.mapStorage(
257− store.executions.getByWorkspaceAndId(input.workspaceId, input.executionId),
262+ store.executions.getByScopeAndId(input.scopeId, input.executionId),
258263 );
259264
260265 if (Option.isNone(existing)) {
261266 return yield* errors.notFound(
262267 "Execution not found",
263− `workspaceId=${input.workspaceId} executionId=${input.executionId}`,
268+ `scopeId=${input.scopeId} executionId=${input.executionId}`,
264269 );
265270 }
266271
@@ -270,7 +275,7 @@ const fetchExecution = ( · 3 unmodified lines
270275 const fetchExecutionEnvelope = (
271276 store: ExecutorStateStoreShape,
272277 input: {
273− workspaceId: Execution["workspaceId"];
278+ scopeId: Execution["scopeId"];
274279 executionId: Execution["id"];
275280 operation: OperationErrorsLike;
276281 },
@@ -291,7 +296,7 @@ const fetchExecutionEnvelope = ( · 14 unmodified lines
291296 const waitForExecutionEnvelopeToSettle = (
292297 store: ExecutorStateStoreShape,
293298 input: {
294− workspaceId: Execution["workspaceId"];
299+ scopeId: Execution["scopeId"];
295300 executionId: Execution["id"];
296301 operation: OperationErrorsLike;
297302 previousPendingInteractionId: ExecutionInteraction["id"] | null;
@@ -658,8 +663,8 @@ const runExecutionAttemptWithDependencies = ( · 360 unmodified lines
658663 interactionMode: InteractionMode,
659664 ) =>
660665 executionResolver({
661− workspaceId: execution.workspaceId,
662− accountId: execution.createdByAccountId,
666+ scopeId: execution.scopeId,
667+ actorScopeId: execution.createdByScopeId,
663668 executionId: execution.id,
664669 onElicitation: createHybridOnElicitation({
665670 executorState: store,
@@ -710,7 +715,7 @@ const forkExecutionAttemptWithDependencies = ( · 44 unmodified lines
710715 interactionMode: InteractionMode,
711716 ) =>
712717 Effect.gen(function* () {
713− const runtimeLocalWorkspace = yield* getRuntimeLocalWorkspaceOption();
718+ const runtimeLocalScope = yield* getRuntimeLocalScopeOption();
714719
715720 yield* Effect.sync(() => {
716721 const effect = runExecutionAttemptWithDependencies(
@@ -722,7 +727,7 @@ const forkExecutionAttemptWithDependencies = ( · 5 unmodified lines
722727 );
723728
724729 Effect.runFork(
725− provideOptionalRuntimeLocalWorkspace(effect, runtimeLocalWorkspace),
730+ provideOptionalRuntimeLocalScope(effect, runtimeLocalScope),
726731 );
727732 });
728733 });
@@ -808,9 +813,9 @@ const createExecutionWithDependencies = ( · 79 unmodified lines
808813 executionResolver: ResolveExecutionEnvironment,
809814 liveExecutionManager: LiveExecutionManager,
810815 input: {
811− workspaceId: WorkspaceId;
816+ scopeId: ScopeId;
812817 payload: CreateExecutionPayload;
813− createdByAccountId: AccountId;
818+ createdByScopeId: ScopeId;
814819 },
815820 ) =>
816821 Effect.gen(function* () {
@@ -818,8 +823,8 @@ const createExecutionWithDependencies = ( · 1 unmodified line
818823 const now = Date.now();
819824 const execution: Execution = {
820825 id: ExecutionIdSchema.make(`exec_${crypto.randomUUID()}`),
821− workspaceId: input.workspaceId,
822− createdByAccountId: input.createdByAccountId,
826+ scopeId: input.scopeId,
827+ createdByScopeId: input.createdByScopeId,
823828 status: "pending",
824829 code,
825830 resultJson: null,
@@ -863,16 +868,16 @@ const createExecutionWithDependencies = ( · 37 unmodified lines
863868 yield* Deferred.await(nextState);
864869
865870 return yield* fetchExecutionEnvelope(store, {
866− workspaceId: input.workspaceId,
871+ scopeId: input.scopeId,
867872 executionId: execution.id,
868873 operation: executionOps.create,
869874 });
870875 });
871876
872877 export const createExecution = (input: {
873− workspaceId: WorkspaceId;
878+ scopeId: ScopeId;
874879 payload: CreateExecutionPayload;
875− createdByAccountId: AccountId;
880+ createdByScopeId: ScopeId;
876881 }) =>
877882 Effect.gen(function* () {
878883 const store = yield* ExecutorStateStore;
@@ -888,12 +893,12 @@ export const createExecution = (input: { · 9 unmodified lines
888893 });
889894
890895 export const getExecution = (input: {
891− workspaceId: WorkspaceId;
896+ scopeId: ScopeId;
892897 executionId: ExecutionId;
893898 }) =>
894899 Effect.flatMap(ExecutorStateStore, (store) =>
895900 fetchExecutionEnvelope(store, {
896− workspaceId: input.workspaceId,
901+ scopeId: input.scopeId,
897902 executionId: input.executionId,
898903 operation: executionOps.get,
899904 })
@@ -921,10 +926,10 @@ export const submitExecutionInteractionResponse = (input: { · 21 unmodified lines
921926 });
922927
923928 export const resumeExecution = (input: {
924− workspaceId: WorkspaceId;
929+ scopeId: ScopeId;
925930 executionId: ExecutionId;
926931 payload: ResumeExecutionPayload;
927− resumedByAccountId: AccountId;
932+ resumedByScopeId: ScopeId;
928933 }) =>
929934 Effect.gen(function* () {
930935 const store = yield* ExecutorStateStore;
@@ -932,7 +937,7 @@ export const resumeExecution = (input: { · 1 unmodified line
932937 const liveExecutionManager = yield* LiveExecutionManagerService;
933938
934939 const existing = yield* fetchExecutionEnvelope(store, {
935− workspaceId: input.workspaceId,
940+ scopeId: input.scopeId,
936941 executionId: input.executionId,
937942 operation: "executions.resume",
938943 });
@@ -1002,7 +1007,7 @@ export const resumeExecution = (input: { · 63 unmodified lines
10021007 }
10031008
10041009 return yield* waitForExecutionEnvelopeToSettle(store, {
1005− workspaceId: input.workspaceId,
1010+ scopeId: input.scopeId,
10061011 executionId: input.executionId,
10071012 operation: executionOps.resume,
10081013 previousPendingInteractionId: existing.pendingInteraction?.id ?? null,
packages/platform/sdk/src/runtime/execution/state.ts+6−3
@@ -4,7 +4,10 @@ import type { · 3 unmodified lines
44 ToolCatalog,
55 ToolInvoker,
66 } from "@executor/codemode-core";
7−import type { AccountId, ExecutionId, WorkspaceId } from "#schema";
7+import type {
8+ ScopeId,
9+ ExecutionId,
10+} from "#schema";
811 import * as Data from "effect/Data";
912 import type * as Effect from "effect/Effect";
1013
@@ -15,8 +18,8 @@ export type ExecutionEnvironment = { · 4 unmodified lines
1518 };
1619
1720 export type ResolveExecutionEnvironment = (input: {
18− workspaceId: WorkspaceId;
19− accountId: AccountId;
21+ scopeId: ScopeId;
22+ actorScopeId: ScopeId;
2023 executionId: ExecutionId;
2124 onElicitation?: OnElicitation;
2225 }) => Effect.Effect<ExecutionEnvironment, unknown>;
packages/platform/sdk/src/runtime/execution/test-http-client.ts+4−2
@@ -3,7 +3,9 @@ import { · 2 unmodified lines
33 HttpApiClient,
44 HttpServer,
55 } from "@effect/platform";
6−import { NodeHttpServer } from "@effect/platform-node";
6+import {
7+ NodeHttpServer,
8+} from "@effect/platform-node";
79 import {
810 ExecutorApi,
911 createExecutorApiLayer,
@@ -93,7 +95,7 @@ type ExecutorApiClient = Effect.Effect.Success< · 83 unmodified lines
9395 export const withExecutorApiClient = <A, E>(
9496 input: {
9597 runtime: ExecutorRuntime;
96− accountId?: string;
98+ actorScopeId?: string;
9799 },
98100 f: (client: ExecutorApiClient) => Effect.Effect<A, E, never>,
99101 ): Effect.Effect<A, E, never> =>
packages/platform/sdk/src/runtime/execution/workspace/local.ts+0−12
@@ -1,12 +0,0 @@
1−import * as Effect from "effect/Effect";
2−
3−import {
4− provideOptionalRuntimeLocalWorkspace,
5− type RuntimeLocalWorkspaceState,
6−} from "../../workspace/runtime-context";
7−
8−export const provideRuntimeLocalWorkspace = <A, E, R>(
9− effect: Effect.Effect<A, E, R>,
10− runtimeLocalWorkspace: RuntimeLocalWorkspaceState | null,
11−): Effect.Effect<A, E, R> =>
12− provideOptionalRuntimeLocalWorkspace(effect, runtimeLocalWorkspace);
packages/platform/sdk/src/runtime/executor-runtime.test.ts+195−195
@@ -1,14 +1,35 @@
1−import { createServer } from "node:http";
2−import { join } from "node:path";
1+import {
2+ createServer,
3+} from "node:http";
4+import {
5+ join,
6+} from "node:path";
37
4−import { FileSystem } from "@effect/platform";
5−import { NodeFileSystem } from "@effect/platform-node";
6−import { describe, expect, it } from "@effect/vitest";
7−import { assertTrue } from "@effect/vitest/utils";
8+import {
9+ FileSystem,
10+ HttpApi,
11+ HttpApiEndpoint,
12+ HttpApiGroup,
13+ HttpApiSchema,
14+ HttpApiBuilder,
15+} from "@effect/platform";
16+import {
17+ NodeFileSystem,
18+} from "@effect/platform-node";
19+import {
20+ describe,
21+ expect,
22+ it,
23+} from "@effect/vitest";
24+import {
25+ assertTrue,
26+} from "@effect/vitest/utils";
827 import * as Either from "effect/Either";
928 import * as Effect from "effect/Effect";
1029 import * as Fiber from "effect/Fiber";
30+import * as Layer from "effect/Layer";
1131 import * as Option from "effect/Option";
32+import * as Schema from "effect/Schema";
1233
1334 import {
1435 ExecutionIdSchema,
@@ -18,12 +39,23 @@ import { · 3 unmodified lines
1839 SecretMaterialIdSchema,
1940 SourceAuthSessionIdSchema,
2041 SourceIdSchema,
21− WorkspaceOauthClientIdSchema,
42+ ScopeOauthClientIdSchema,
2243 } from "#schema";
23−import type { ToolPath } from "@executor/codemode-core";
24−import { createCatalogImportMetadata } from "@executor/source-core";
25−import { createGraphqlCatalogFragment } from "@executor/source-graphql";
26−import { createWorkspaceExecutorAdminToolMap } from "../../../internal/src/index";
44+import type {
45+ ToolPath,
46+} from "@executor/codemode-core";
47+import {
48+ makeOpenApiTestServer,
49+} from "@executor/effect-test-utils";
50+import {
51+ createCatalogImportMetadata,
52+} from "@executor/source-core";
53+import {
54+ createGraphqlCatalogFragment,
55+} from "@executor/source-graphql";
56+import {
57+ createWorkspaceExecutorAdminToolMap,
58+} from "../../../internal/src/index";
2759
2860 import {
2961 type ExecutorRuntime,
@@ -31,14 +63,22 @@ import { · 1 unmodified line
3163 provideExecutorRuntime,
3264 RuntimeExecutionResolverService,
3365 } from "./index";
34−import { createSourceFromPayload } from "./sources/source-definitions";
35−import { decodeSourceCredentialSelectionContent } from "./sources/source-credential-interactions";
36−import { persistSource } from "./sources/source-store";
66+import {
67+ createSourceFromPayload,
68+} from "./sources/source-definitions";
69+import {
70+ decodeSourceCredentialSelectionContent,
71+} from "./sources/source-credential-interactions";
72+import {
73+ persistSource,
74+} from "./sources/source-store";
3775 import {
3876 withExecutorApiClient,
3977 withExecutorApiRequestHandler,
4078 } from "./execution/test-http-client";
41−import { runtimeEffectError } from "./effect-errors";
79+import {
80+ runtimeEffectError,
81+} from "./effect-errors";
4282 import {
4383 buildLocalSourceArtifact,
4484 createLocalExecutorRuntime as createExecutorRuntime,
@@ -57,15 +97,15 @@ import { · 12 unmodified lines
5797
5898 const makeRuntime = Effect.gen(function* () {
5999 const fs = yield* FileSystem.FileSystem;
60− const workspaceRoot = yield* fs.makeTempDirectoryScoped({
100+ const scopeRoot = yield* fs.makeTempDirectoryScoped({
61101 prefix: "executor-runtime-",
62102 });
63− const homeConfigPath = join(workspaceRoot, ".executor-home.jsonc");
64− const homeStateDirectory = join(workspaceRoot, ".executor-home-state");
103+ const homeConfigPath = join(scopeRoot, ".executor-home.jsonc");
104+ const homeStateDirectory = join(scopeRoot, ".executor-home-state");
65105
66106 return yield* Effect.acquireRelease(
67107 createExecutorRuntime({
68− workspaceRoot,
108+ workspaceRoot: scopeRoot,
69109 homeConfigPath,
70110 homeStateDirectory,
71111 createInternalToolMap: createWorkspaceExecutorAdminToolMap,
@@ -74,11 +114,19 @@ const makeRuntime = Effect.gen(function* () { · 2 unmodified lines
74114 );
75115 }).pipe(Effect.provide(NodeFileSystem.layer));
76116
77−type OpenApiSpecServer = {
78− baseUrl: string;
79− specUrl: string;
80− close: () => Promise<void>;
81−};
117+const ownerParam = HttpApiSchema.param("owner", Schema.String);
118+const repoParam = HttpApiSchema.param("repo", Schema.String);
119+
120+class RuntimeTestReposApi extends HttpApiGroup.make("repos")
121+ .add(
122+ HttpApiEndpoint.get("getRepo")`/repos/${ownerParam}/${repoParam}`
123+ .addSuccess(Schema.Unknown),
124+ )
125+{}
126+
127+class RuntimeTestOpenApi extends HttpApi.make("runtimeTest").add(
128+ RuntimeTestReposApi,
129+) {}
82130
83131 type GoogleWorkspaceTestServer = {
84132 baseUrl: string;
@@ -99,74 +147,26 @@ type GoogleWorkspaceTestServer = { · 14 unmodified lines
99147 close: () => Promise<void>;
100148 };
101149
102−const makeOpenApiSpecServer = Effect.acquireRelease(
103− Effect.promise<OpenApiSpecServer>(
104− () =>
105− new Promise<OpenApiSpecServer>((resolve, reject) => {
106− const openApiDocument = JSON.stringify({
107− openapi: "3.0.3",
108− info: {
109− title: "GitHub Test API",
110− version: "1.0.0",
111− },
112− paths: {
113− "/repos/{owner}/{repo}": {
114− get: {
115− operationId: "repos/get-repo",
116− tags: ["repos"],
117− summary: "Get a repository",
118− responses: {
119− 200: {
120− description: "ok",
121− },
122− },
123− },
124− },
125− },
126− });
127−
128− const server = createServer((request, response) => {
129− if (request.url !== "/openapi.json") {
130− response.statusCode = 404;
131− response.end();
132− return;
133− }
134−
135− response.statusCode = 200;
136− response.setHeader("content-type", "application/json");
137− response.end(openApiDocument);
138− });
139−
140− server.once("error", reject);
141− server.listen(0, "127.0.0.1", () => {
142− const address = server.address();
143− if (!address || typeof address === "string") {
144− reject(new Error("Failed to bind OpenAPI runtime test server"));
145− return;
146− }
147−
148− const baseUrl = `http://127.0.0.1:${address.port}`;
149− resolve({
150− baseUrl,
151− specUrl: `${baseUrl}/openapi.json`,
152− close: () =>
153− new Promise<void>((closeResolve, closeReject) => {
154− server.close((error) => {
155− if (error) {
156− closeReject(error);
157− return;
158− }
159−
160− closeResolve();
161− });
162− }),
163− });
164− });
150+const runtimeTestOpenApiLive = HttpApiBuilder.group(
151+ RuntimeTestOpenApi,
152+ "repos",
153+ (handlers) =>
154+ handlers.handle("getRepo", ({ path }) =>
155+ Effect.succeed({
156+ full_name: `${path.owner}/${path.repo}`,
157+ private: false,
165158 }),
166− ),
167− (server) => Effect.promise(() => server.close()).pipe(Effect.orDie),
159+ ),
168160 );
169161
162+const runtimeTestOpenApiLayer = HttpApiBuilder.api(RuntimeTestOpenApi).pipe(
163+ Layer.provide(runtimeTestOpenApiLive),
164+);
165+
166+const makeOpenApiSpecServer = makeOpenApiTestServer({
167+ apiLayer: runtimeTestOpenApiLayer,
168+});
169+
170170 const makeGoogleWorkspaceTestServer = Effect.acquireRelease(
171171 Effect.promise<GoogleWorkspaceTestServer>(
172172 () =>
@@ -379,7 +379,7 @@ const createPersistedGoogleSource = (input: { · 206 unmodified lines
379379 Effect.gen(function* () {
380380 const installation = input.runtime.localInstallation;
381381 const source = yield* createSourceFromPayload({
382− workspaceId: installation.workspaceId,
382+ scopeId: installation.scopeId,
383383 sourceId: SourceIdSchema.make(`src_${crypto.randomUUID()}`),
384384 payload: {
385385 name: input.name,
@@ -402,7 +402,7 @@ const createPersistedGoogleSource = (input: { · 16 unmodified lines
402402 });
403403
404404 return yield* persistSource(input.runtime.storage.executorState, source, {
405− actorAccountId: installation.accountId,
405+ actorScopeId: installation.actorScopeId,
406406 }).pipe((effect) => provideExecutorRuntime(effect, input.runtime));
407407 });
408408
@@ -430,8 +430,8 @@ const invokeWorkspaceTool = <A>(input: { · 21 unmodified lines
430430 const installation = input.runtime.localInstallation;
431431 const resolveEnvironment = yield* RuntimeExecutionResolverService;
432432 const environment = yield* resolveEnvironment({
433− workspaceId: installation.workspaceId,
434− accountId: installation.accountId,
433+ scopeId: installation.scopeId,
434+ actorScopeId: installation.actorScopeId,
435435 executionId: ExecutionIdSchema.make(`exec_tool_${crypto.randomUUID()}`),
436436 });
437437
@@ -449,14 +449,14 @@ describe("executor-runtime", () => { · 11 unmodified lines
449449 () =>
450450 Effect.gen(function* () {
451451 const fs = yield* FileSystem.FileSystem;
452− const workspaceRoot = yield* fs.makeTempDirectoryScoped({
452+ const scopeRoot = yield* fs.makeTempDirectoryScoped({
453453 prefix: "executor-local-config-runtime-",
454454 });
455− const homeConfigPath = join(workspaceRoot, ".executor-home.jsonc");
456− const homeStateDirectory = join(workspaceRoot, ".executor-home-state");
455+ const homeConfigPath = join(scopeRoot, ".executor-home.jsonc");
456+ const homeStateDirectory = join(scopeRoot, ".executor-home-state");
457457 const runtime = yield* Effect.acquireRelease(
458458 createExecutorRuntime({
459− workspaceRoot,
459+ workspaceRoot: scopeRoot,
460460 homeConfigPath,
461461 homeStateDirectory,
462462 }),
@@ -467,10 +467,10 @@ describe("executor-runtime", () => { · 4 unmodified lines
467467 const installation = runtime.localInstallation;
468468
469469 const createdSource = yield* withExecutorApiClient(
470− { runtime, accountId: installation.accountId },
470+ { runtime, actorScopeId: installation.actorScopeId },
471471 (client) =>
472472 client.sources.create({
473− path: { workspaceId: installation.workspaceId },
473+ path: { workspaceId: installation.scopeId },
474474 payload: {
475475 name: "GitHub",
476476 kind: "openapi",
@@ -485,7 +485,7 @@ describe("executor-runtime", () => { · 8 unmodified lines
485485 }),
486486 );
487487
488− const configPath = join(workspaceRoot, ".executor", "executor.jsonc");
488+ const configPath = join(scopeRoot, ".executor", "executor.jsonc");
489489 const createdConfig = JSON.parse(
490490 yield* fs.readFileString(configPath, "utf8"),
491491 ) as {
@@ -500,11 +500,11 @@ describe("executor-runtime", () => { · 8 unmodified lines
500500 );
501501
502502 const removed = yield* withExecutorApiClient(
503− { runtime, accountId: installation.accountId },
503+ { runtime, actorScopeId: installation.actorScopeId },
504504 (client) =>
505505 client.sources.remove({
506506 path: {
507− workspaceId: installation.workspaceId,
507+ workspaceId: installation.scopeId,
508508 sourceId: createdSource.id,
509509 },
510510 }),
@@ -615,11 +615,11 @@ describe("executor-runtime", () => { · 104 unmodified lines
615615 const openApiServer = yield* makeOpenApiSpecServer;
616616 const installation = runtime.localInstallation;
617617 const createdSource = yield* withExecutorApiClient(
618− { runtime, accountId: installation.accountId },
618+ { runtime, actorScopeId: installation.actorScopeId },
619619 (client) =>
620620 client.sources.create({
621621 path: {
622− workspaceId: installation.workspaceId,
622+ workspaceId: installation.scopeId,
623623 },
624624 payload: {
625625 name: "GitHub",
@@ -640,15 +640,15 @@ describe("executor-runtime", () => { · 14 unmodified lines
640640 );
641641
642642 const localInstallation = yield* invokeWorkspaceTool<{
643− workspaceId: string;
644− accountId: string;
643+ scopeId: string;
644+ actorScopeId: string;
645645 }>({
646646 runtime,
647647 path: "executor.local.installation.get" as ToolPath,
648648 args: {},
649649 });
650− expect(localInstallation.workspaceId).toBe(installation.workspaceId);
651− expect(localInstallation.accountId).toBe(installation.accountId);
650+ expect(localInstallation.scopeId).toBe(installation.scopeId);
651+ expect(localInstallation.actorScopeId).toBe(installation.actorScopeId);
652652
653653 const localConfig = yield* invokeWorkspaceTool<{
654654 defaultSecretStoreProvider: string;
@@ -723,11 +723,11 @@ describe("executor-runtime", () => { · 68 unmodified lines
723723 expect(loadedPolicy.id).toBe(createdPolicy.id);
724724
725725 const listedPolicies = yield* withExecutorApiClient(
726− { runtime, accountId: installation.accountId },
726+ { runtime, actorScopeId: installation.actorScopeId },
727727 (client) =>
728728 client.policies.list({
729729 path: {
730− workspaceId: installation.workspaceId,
730+ workspaceId: installation.scopeId,
731731 },
732732 }),
733733 );
@@ -768,11 +768,11 @@ describe("executor-runtime", () => { · 34 unmodified lines
768768 expect(removedSource.removed).toBe(true);
769769
770770 const apiSources = yield* withExecutorApiClient(
771− { runtime, accountId: installation.accountId },
771+ { runtime, actorScopeId: installation.actorScopeId },
772772 (client) =>
773773 client.sources.list({
774774 path: {
775− workspaceId: installation.workspaceId,
775+ workspaceId: installation.scopeId,
776776 },
777777 }),
778778 );
@@ -798,8 +798,8 @@ describe("executor-runtime", () => { · 19 unmodified lines
798798
799799 yield* runtime.storage.executorState.executions.insert({
800800 id: executionId,
801− workspaceId: installation.workspaceId,
802− createdByAccountId: installation.accountId,
801+ scopeId: installation.scopeId,
802+ createdByScopeId: installation.actorScopeId,
803803 status: "running",
804804 code: "return await tools.executor.sources.add(...)",
805805 resultJson: null,
@@ -812,7 +812,7 @@ describe("executor-runtime", () => { · 6 unmodified lines
812812 });
813813
814814 const localSource = yield* createSourceFromPayload({
815− workspaceId: installation.workspaceId,
815+ scopeId: installation.scopeId,
816816 sourceId,
817817 payload: {
818818 name: "GitHub",
@@ -832,7 +832,7 @@ describe("executor-runtime", () => { · 13 unmodified lines
832832 now,
833833 }).pipe(Effect.orDie);
834834 yield* persistSource(runtime.storage.executorState, localSource, {
835− actorAccountId: installation.accountId,
835+ actorScopeId: installation.actorScopeId,
836836 }).pipe(
837837 (effect) => provideExecutorRuntime(effect, runtime),
838838 Effect.orDie,
@@ -854,13 +854,13 @@ describe("executor-runtime", () => { · 15 unmodified lines
854854 endpoint: "https://api.github.com",
855855 specUrl: "https://example.com/github-openapi.yaml",
856856 name: "GitHub",
857− workspaceId: installation.workspaceId,
857+ scopeId: installation.scopeId,
858858 sourceId,
859859 },
860860 elicitation: {
861861 mode: "url",
862862 message: "Open the secure credential page to connect GitHub",
863− url: `http://127.0.0.1/v1/workspaces/${encodeURIComponent(installation.workspaceId)}/sources/${encodeURIComponent(sourceId)}/credentials?interactionId=${encodeURIComponent(interactionId)}`,
863+ url: `http://127.0.0.1/v1/workspaces/${encodeURIComponent(installation.scopeId)}/sources/${encodeURIComponent(sourceId)}/credentials?interactionId=${encodeURIComponent(interactionId)}`,
864864 elicitationId: interactionSuffix,
865865 },
866866 });
@@ -893,7 +893,7 @@ describe("executor-runtime", () => { · 26 unmodified lines
893893 (handleRequest) =>
894894 Effect.promise(async () => {
895895 const url = new URL(
896− `/v1/workspaces/${encodeURIComponent(installation.workspaceId)}/sources/${encodeURIComponent(sourceId)}/credentials`,
896+ `/v1/workspaces/${encodeURIComponent(installation.scopeId)}/sources/${encodeURIComponent(sourceId)}/credentials`,
897897 "http://127.0.0.1",
898898 );
899899 url.searchParams.set("interactionId", pendingInteraction.value.id);
@@ -910,7 +910,7 @@ describe("executor-runtime", () => { · 10 unmodified lines
910910 (handleRequest) =>
911911 Effect.promise(async () => {
912912 const url = new URL(
913− `/v1/workspaces/${encodeURIComponent(installation.workspaceId)}/sources/${encodeURIComponent(sourceId)}/credentials`,
913+ `/v1/workspaces/${encodeURIComponent(installation.scopeId)}/sources/${encodeURIComponent(sourceId)}/credentials`,
914914 "http://127.0.0.1",
915915 );
916916 url.searchParams.set("interactionId", pendingInteraction.value.id);
@@ -978,12 +978,12 @@ describe("executor-runtime", () => { · 61 unmodified lines
978978 it.scoped("loads a v1.2.3-style workspace artifact on startup", () =>
979979 Effect.gen(function* () {
980980 const fs = yield* FileSystem.FileSystem;
981− const workspaceRoot = yield* fs.makeTempDirectoryScoped({
981+ const scopeRoot = yield* fs.makeTempDirectoryScoped({
982982 prefix: "executor-runtime-v123-",
983983 });
984− const homeConfigPath = join(workspaceRoot, ".executor-home.jsonc");
985− const homeStateDirectory = join(workspaceRoot, ".executor-home-state");
986− const configDirectory = join(workspaceRoot, ".executor");
984+ const homeConfigPath = join(scopeRoot, ".executor-home.jsonc");
985+ const homeStateDirectory = join(scopeRoot, ".executor-home-state");
986+ const configDirectory = join(scopeRoot, ".executor");
987987 const configPath = join(configDirectory, "executor.jsonc");
988988 const sourceId = SourceIdSchema.make("graphql");
989989 const now = Date.now();
@@ -1010,13 +1010,13 @@ describe("executor-runtime", () => { · 20 unmodified lines
10101010 );
10111011
10121012 const context = yield* resolveLocalWorkspaceContext({
1013− workspaceRoot,
1013+ workspaceRoot: scopeRoot,
10141014 homeConfigPath,
10151015 homeStateDirectory,
10161016 });
10171017 const installation = deriveLocalInstallation(context);
10181018 const source = yield* createSourceFromPayload({
1019− workspaceId: installation.workspaceId,
1019+ scopeId: installation.scopeId,
10201020 sourceId,
10211021 payload: {
10221022 name: "GraphQL API",
@@ -1108,7 +1108,7 @@ describe("executor-runtime", () => { · 85 unmodified lines
11081108 authArtifacts: [],
11091109 authLeases: [],
11101110 sourceOauthClients: [],
1111− workspaceOauthClients: [],
1111+ scopeOauthClients: [],
11121112 providerAuthGrants: [],
11131113 sourceAuthSessions: [],
11141114 secretMaterials: [],
@@ -1145,7 +1145,7 @@ describe("executor-runtime", () => { · 30 unmodified lines
11451145
11461146 const runtime = yield* Effect.acquireRelease(
11471147 createExecutorRuntime({
1148− workspaceRoot,
1148+ workspaceRoot: scopeRoot,
11491149 homeConfigPath,
11501150 homeStateDirectory,
11511151 }),
@@ -1154,11 +1154,11 @@ describe("executor-runtime", () => { · 2 unmodified lines
11541154 );
11551155
11561156 const inspection = yield* withExecutorApiClient(
1157− { runtime, accountId: installation.accountId },
1157+ { runtime, actorScopeId: installation.actorScopeId },
11581158 (client) =>
11591159 client.sources.inspection({
11601160 path: {
1161− workspaceId: installation.workspaceId,
1161+ workspaceId: installation.scopeId,
11621162 sourceId,
11631163 },
11641164 }),
@@ -1179,13 +1179,13 @@ describe("executor-runtime", () => { · 14 unmodified lines
11791179 Effect.gen(function* () {
11801180 const fs = yield* FileSystem.FileSystem;
11811181 const openApiServer = yield* makeOpenApiSpecServer;
1182− const workspaceRoot = yield* fs.makeTempDirectoryScoped({
1182+ const scopeRoot = yield* fs.makeTempDirectoryScoped({
11831183 prefix: "executor-runtime-startup-rebuild-",
11841184 });
1185− const homeConfigPath = join(workspaceRoot, ".executor-home.jsonc");
1186− const homeStateDirectory = join(workspaceRoot, ".executor-home-state");
1185+ const homeConfigPath = join(scopeRoot, ".executor-home.jsonc");
1186+ const homeStateDirectory = join(scopeRoot, ".executor-home-state");
11871187 const context = yield* resolveLocalWorkspaceContext({
1188− workspaceRoot,
1188+ workspaceRoot: scopeRoot,
11891189 homeConfigPath,
11901190 homeStateDirectory,
11911191 });
@@ -1193,7 +1193,7 @@ describe("executor-runtime", () => { · 1 unmodified line
11931193 const sourceId = SourceIdSchema.make("github");
11941194 const now = Date.now();
11951195 const source = yield* createSourceFromPayload({
1196− workspaceId: installation.workspaceId,
1196+ scopeId: installation.scopeId,
11971197 sourceId,
11981198 payload: {
11991199 name: "GitHub",
@@ -1251,7 +1251,7 @@ describe("executor-runtime", () => { · 51 unmodified lines
12511251
12521252 const runtime = yield* Effect.acquireRelease(
12531253 createExecutorRuntime({
1254− workspaceRoot,
1254+ workspaceRoot: scopeRoot,
12551255 homeConfigPath,
12561256 homeStateDirectory,
12571257 }),
@@ -1266,11 +1266,11 @@ describe("executor-runtime", () => { · 8 unmodified lines
12661266 expect(rebuiltArtifact).not.toBeNull();
12671267
12681268 const inspection = yield* withExecutorApiClient(
1269− { runtime, accountId: installation.accountId },
1269+ { runtime, actorScopeId: installation.actorScopeId },
12701270 (client) =>
12711271 client.sources.inspection({
12721272 path: {
1273− workspaceId: installation.workspaceId,
1273+ workspaceId: installation.scopeId,
12741274 sourceId,
12751275 },
12761276 }),
@@ -1290,7 +1290,7 @@ describe("executor-runtime", () => { · 13 unmodified lines
12901290 const now = Date.now();
12911291
12921292 const localSource = yield* createSourceFromPayload({
1293− workspaceId: installation.workspaceId,
1293+ scopeId: installation.scopeId,
12941294 sourceId,
12951295 payload: {
12961296 name: "Google Drive",
@@ -1314,18 +1314,18 @@ describe("executor-runtime", () => { · 17 unmodified lines
13141314 now,
13151315 }).pipe(Effect.orDie);
13161316 yield* persistSource(runtime.storage.executorState, localSource, {
1317− actorAccountId: installation.accountId,
1317+ actorScopeId: installation.actorScopeId,
13181318 }).pipe(
13191319 (effect) => provideExecutorRuntime(effect, runtime),
13201320 Effect.orDie,
13211321 );
13221322
13231323 const inspection = yield* withExecutorApiClient(
1324− { runtime, accountId: installation.accountId },
1324+ { runtime, actorScopeId: installation.actorScopeId },
13251325 (client) =>
13261326 client.sources.inspection({
13271327 path: {
1328− workspaceId: installation.workspaceId,
1328+ workspaceId: installation.scopeId,
13291329 sourceId,
13301330 },
13311331 }),
@@ -1348,7 +1348,7 @@ describe("executor-runtime", () => { · 16 unmodified lines
13481348 const now = Date.now();
13491349
13501350 const localSource = yield* createSourceFromPayload({
1351− workspaceId: installation.workspaceId,
1351+ scopeId: installation.scopeId,
13521352 sourceId,
13531353 payload: {
13541354 name: "Google Drive",
@@ -1372,7 +1372,7 @@ describe("executor-runtime", () => { · 17 unmodified lines
13721372 now,
13731373 }).pipe(Effect.orDie);
13741374 yield* persistSource(runtime.storage.executorState, localSource, {
1375− actorAccountId: installation.accountId,
1375+ actorScopeId: installation.actorScopeId,
13761376 }).pipe(
13771377 (effect) => provideExecutorRuntime(effect, runtime),
13781378 Effect.orDie,
@@ -1380,11 +1380,11 @@ describe("executor-runtime", () => { · 1 unmodified line
13801380
13811381 const error = yield* expectLeft(
13821382 withExecutorApiClient(
1383− { runtime, accountId: installation.accountId },
1383+ { runtime, actorScopeId: installation.actorScopeId },
13841384 (client) =>
13851385 client.sources.inspection({
13861386 path: {
1387− workspaceId: installation.workspaceId,
1387+ workspaceId: installation.scopeId,
13881388 sourceId,
13891389 },
13901390 }),
@@ -1415,8 +1415,8 @@ describe("executor-runtime", () => { · 24 unmodified lines
14151415
14161416 yield* runtime.storage.executorState.executions.insert({
14171417 id: executionId,
1418− workspaceId: installation.workspaceId,
1419− createdByAccountId: installation.accountId,
1418+ scopeId: installation.scopeId,
1419+ createdByScopeId: installation.actorScopeId,
14201420 status: "running",
14211421 code: "return await tools.executor.sources.add(...)",
14221422 resultJson: null,
@@ -1429,7 +1429,7 @@ describe("executor-runtime", () => { · 6 unmodified lines
14291429 });
14301430
14311431 const localSource = yield* createSourceFromPayload({
1432− workspaceId: installation.workspaceId,
1432+ scopeId: installation.scopeId,
14331433 sourceId,
14341434 payload: {
14351435 name: "GitHub",
@@ -1449,7 +1449,7 @@ describe("executor-runtime", () => { · 13 unmodified lines
14491449 now,
14501450 }).pipe(Effect.orDie);
14511451 yield* persistSource(runtime.storage.executorState, localSource, {
1452− actorAccountId: installation.accountId,
1452+ actorScopeId: installation.actorScopeId,
14531453 }).pipe(
14541454 (effect) => provideExecutorRuntime(effect, runtime),
14551455 Effect.orDie,
@@ -1471,13 +1471,13 @@ describe("executor-runtime", () => { · 15 unmodified lines
14711471 endpoint: "https://api.github.com",
14721472 specUrl: "https://example.com/github-openapi.yaml",
14731473 name: "GitHub",
1474− workspaceId: installation.workspaceId,
1474+ scopeId: installation.scopeId,
14751475 sourceId,
14761476 },
14771477 elicitation: {
14781478 mode: "url",
14791479 message: "Open the secure credential page to connect GitHub",
1480− url: `http://127.0.0.1/v1/workspaces/${encodeURIComponent(installation.workspaceId)}/sources/${encodeURIComponent(sourceId)}/credentials?interactionId=${encodeURIComponent(interactionId)}`,
1480+ url: `http://127.0.0.1/v1/workspaces/${encodeURIComponent(installation.scopeId)}/sources/${encodeURIComponent(sourceId)}/credentials?interactionId=${encodeURIComponent(interactionId)}`,
14811481 elicitationId: interactionSuffix,
14821482 },
14831483 });
@@ -1505,7 +1505,7 @@ describe("executor-runtime", () => { · 21 unmodified lines
15051505 assertTrue(Option.isSome(pendingInteraction));
15061506
15071507 const submitted = yield* submitSourceCredentialInteraction({
1508− workspaceId: installation.workspaceId,
1508+ scopeId: installation.scopeId,
15091509 sourceId,
15101510 interactionId: pendingInteraction.value.id,
15111511 action: "continue",
@@ -1538,7 +1538,7 @@ describe("executor-runtime", () => { · 26 unmodified lines
15381538 const executionId = ExecutionIdSchema.make(
15391539 "exec_local_graphql_credential_continue",
15401540 );
1541− const workspaceId = "ws_local_graphql" as const;
1541+ const scopeId = "ws_local_graphql" as const;
15421542 const sourceId = SourceIdSchema.make("graphql_api");
15431543 const now = Date.now();
15441544 const decoded = decodeSourceCredentialInteraction({
@@ -1555,7 +1555,7 @@ describe("executor-runtime", () => { · 10 unmodified lines
15551555 endpoint: "https://example.com/graphql",
15561556 name: "GraphQL API",
15571557 namespace: "graphql",
1558− workspaceId,
1558+ scopeId,
15591559 sourceId,
15601560 },
15611561 elicitation: {
@@ -1570,7 +1570,7 @@ describe("executor-runtime", () => { · 8 unmodified lines
15701570 });
15711571
15721572 expect(decoded).not.toBeNull();
1573− expect(decoded?.workspaceId).toBe(workspaceId);
1573+ expect(decoded?.scopeId).toBe(scopeId);
15741574 expect(decoded?.sourceId).toBe(sourceId);
15751575 expect(decoded?.message).toContain("GraphQL API");
15761576 }),
@@ -1589,7 +1589,7 @@ describe("executor-runtime", () => { · 12 unmodified lines
15891589 (client) =>
15901590 client.sources.createWorkspaceOauthClient({
15911591 path: {
1592− workspaceId: installation.workspaceId,
1592+ workspaceId: installation.scopeId,
15931593 },
15941594 payload: {
15951595 providerKey: "google_workspace",
@@ -1615,8 +1615,8 @@ describe("executor-runtime", () => { · 19 unmodified lines
16151615 );
16161616 yield* runtime.storage.executorState.providerAuthGrants.upsert({
16171617 id: grantId,
1618− workspaceId: installation.workspaceId,
1619− actorAccountId: installation.accountId,
1618+ scopeId: installation.scopeId,
1619+ actorScopeId: installation.actorScopeId,
16201620 providerKey: "google_workspace",
16211621 oauthClientId: oauthClient.id,
16221622 tokenEndpoint: googleServer.tokenEndpoint,
@@ -1642,11 +1642,11 @@ describe("executor-runtime", () => { · 19 unmodified lines
16421642 const result = yield* withExecutorApiClient({ runtime }, (client) =>
16431643 client.sources.connect({
16441644 path: {
1645− workspaceId: installation.workspaceId,
1645+ workspaceId: installation.scopeId,
16461646 },
16471647 payload: {
16481648 kind: "google_discovery",
1649− workspaceOauthClientId: oauthClient.id,
1649+ scopeOauthClientId: oauthClient.id,
16501650 service: "drive",
16511651 version: "v3",
16521652 discoveryUrl: googleServer.discoveryUrl({
@@ -1698,7 +1698,7 @@ describe("executor-runtime", () => { · 45 unmodified lines
16981698 (client) =>
16991699 client.sources.createWorkspaceOauthClient({
17001700 path: {
1701− workspaceId: installation.workspaceId,
1701+ workspaceId: installation.scopeId,
17021702 },
17031703 payload: {
17041704 providerKey: "google_workspace",
@@ -1714,11 +1714,11 @@ describe("executor-runtime", () => { · 9 unmodified lines
17141714 const result = yield* withExecutorApiClient({ runtime }, (client) =>
17151715 client.sources.connect({
17161716 path: {
1717− workspaceId: installation.workspaceId,
1717+ workspaceId: installation.scopeId,
17181718 },
17191719 payload: {
17201720 kind: "google_discovery",
1721− workspaceOauthClientId: oauthClient.id,
1721+ scopeOauthClientId: oauthClient.id,
17221722 service: "gmail",
17231723 version: "v1",
17241724 discoveryUrl: googleServer.discoveryUrl({
@@ -1745,7 +1745,7 @@ describe("executor-runtime", () => { · 20 unmodified lines
17451745 );
17461746 expect(redirectUri).not.toBeNull();
17471747 expect(new URL(redirectUri!).pathname).toBe(
1748− `/v1/workspaces/${encodeURIComponent(installation.workspaceId)}/oauth/provider/callback`,
1748+ `/v1/workspaces/${encodeURIComponent(installation.scopeId)}/oauth/provider/callback`,
17491749 );
17501750 expect(result.source.status).toBe("auth_required");
17511751 }),
@@ -1765,7 +1765,7 @@ describe("executor-runtime", () => { · 13 unmodified lines
17651765 (client) =>
17661766 client.sources.createWorkspaceOauthClient({
17671767 path: {
1768− workspaceId: installation.workspaceId,
1768+ workspaceId: installation.scopeId,
17691769 },
17701770 payload: {
17711771 providerKey: "google_workspace",
@@ -1791,8 +1791,8 @@ describe("executor-runtime", () => { · 19 unmodified lines
17911791 );
17921792 yield* runtime.storage.executorState.providerAuthGrants.upsert({
17931793 id: grantId,
1794− workspaceId: installation.workspaceId,
1795− actorAccountId: installation.accountId,
1794+ scopeId: installation.scopeId,
1795+ actorScopeId: installation.actorScopeId,
17961796 providerKey: "google_workspace",
17971797 oauthClientId: oauthClient.id,
17981798 tokenEndpoint: googleServer.tokenEndpoint,
@@ -1822,10 +1822,10 @@ describe("executor-runtime", () => { · 23 unmodified lines
18221822 const result = yield* withExecutorApiClient({ runtime }, (client) =>
18231823 client.sources.connectBatch({
18241824 path: {
1825− workspaceId: installation.workspaceId,
1825+ workspaceId: installation.scopeId,
18261826 },
18271827 payload: {
1828− workspaceOauthClientId: oauthClient.id,
1828+ scopeOauthClientId: oauthClient.id,
18291829 sources: [
18301830 {
18311831 service: "gmail",
@@ -1894,7 +1894,7 @@ describe("executor-runtime", () => { · 62 unmodified lines
18941894 (client) =>
18951895 client.sources.createWorkspaceOauthClient({
18961896 path: {
1897− workspaceId: installation.workspaceId,
1897+ workspaceId: installation.scopeId,
18981898 },
18991899 payload: {
19001900 providerKey: "google_workspace",
@@ -1910,10 +1910,10 @@ describe("executor-runtime", () => { · 9 unmodified lines
19101910 const result = yield* withExecutorApiClient({ runtime }, (client) =>
19111911 client.sources.connectBatch({
19121912 path: {
1913− workspaceId: installation.workspaceId,
1913+ workspaceId: installation.scopeId,
19141914 },
19151915 payload: {
1916− workspaceOauthClientId: oauthClient.id,
1916+ scopeOauthClientId: oauthClient.id,
19171917 sources: [
19181918 {
19191919 service: "gmail",
@@ -1939,7 +1939,7 @@ describe("executor-runtime", () => { · 19 unmodified lines
19391939
19401940 expect(redirectUri).not.toBeNull();
19411941 expect(new URL(redirectUri!).pathname).toBe(
1942− `/v1/workspaces/${encodeURIComponent(installation.workspaceId)}/oauth/provider/callback`,
1942+ `/v1/workspaces/${encodeURIComponent(installation.scopeId)}/oauth/provider/callback`,
19431943 );
19441944 }),
19451945 60_000,
@@ -1958,7 +1958,7 @@ describe("executor-runtime", () => { · 12 unmodified lines
19581958 (client) =>
19591959 client.sources.createWorkspaceOauthClient({
19601960 path: {
1961− workspaceId: installation.workspaceId,
1961+ workspaceId: installation.scopeId,
19621962 },
19631963 payload: {
19641964 providerKey: "google_workspace",
@@ -2001,8 +2001,8 @@ describe("executor-runtime", () => { · 36 unmodified lines
20012001 );
20022002 yield* runtime.storage.executorState.providerAuthGrants.upsert({
20032003 id: grantId,
2004− workspaceId: installation.workspaceId,
2005− actorAccountId: installation.accountId,
2004+ scopeId: installation.scopeId,
2005+ actorScopeId: installation.actorScopeId,
20062006 providerKey: "google_workspace",
20072007 oauthClientId: oauthClient.id,
20082008 tokenEndpoint: googleServer.tokenEndpoint,
@@ -2026,11 +2026,11 @@ describe("executor-runtime", () => { · 17 unmodified lines
20262026 const state = `provider-state-${crypto.randomUUID()}`;
20272027 yield* runtime.storage.executorState.sourceAuthSessions.upsert({
20282028 id: sessionId,
2029− workspaceId: installation.workspaceId,
2029+ scopeId: installation.scopeId,
20302030 sourceId: SourceIdSchema.make(
20312031 `oauth_provider_${crypto.randomUUID()}`,
20322032 ),
2033− actorAccountId: installation.accountId,
2033+ actorScopeId: installation.actorScopeId,
20342034 credentialSlot: "runtime",
20352035 executionId: null,
20362036 interactionId: null,
@@ -2083,7 +2083,7 @@ describe("executor-runtime", () => { · 46 unmodified lines
20832083 (client) =>
20842084 client.sources.providerOauthComplete({
20852085 path: {
2086− workspaceId: installation.workspaceId,
2086+ workspaceId: installation.scopeId,
20872087 },
20882088 urlParams: {
20892089 state,
@@ -2111,7 +2111,7 @@ describe("executor-runtime", () => { · 21 unmodified lines
21112111 (client) =>
21122112 client.sources.get({
21132113 path: {
2114− workspaceId: installation.workspaceId,
2114+ workspaceId: installation.scopeId,
21152115 sourceId: source.id,
21162116 },
21172117 }),
@@ -2148,14 +2148,14 @@ describe("executor-runtime", () => { · 30 unmodified lines
21482148 Effect.gen(function* () {
21492149 const runtime = yield* makeRuntime;
21502150 const installation = runtime.localInstallation;
2151− const oauthClientId = WorkspaceOauthClientIdSchema.make(
2151+ const oauthClientId = ScopeOauthClientIdSchema.make(
21522152 `ws_oauth_client_${crypto.randomUUID()}`,
21532153 );
21542154 const refreshSecretId = "sec_google_refresh_orphan";
21552155
2156− yield* runtime.storage.executorState.workspaceOauthClients.upsert({
2156+ yield* runtime.storage.executorState.scopeOauthClients.upsert({
21572157 id: oauthClientId,
2158− workspaceId: installation.workspaceId,
2158+ scopeId: installation.scopeId,
21592159 providerKey: "google_workspace",
21602160 label: "Local Google Workspace Client",
21612161 clientId: "google-client-id",
@@ -2177,8 +2177,8 @@ describe("executor-runtime", () => { · 15 unmodified lines
21772177 );
21782178 yield* runtime.storage.executorState.providerAuthGrants.upsert({
21792179 id: grantId,
2180− workspaceId: installation.workspaceId,
2181− actorAccountId: installation.accountId,
2180+ scopeId: installation.scopeId,
2181+ actorScopeId: installation.actorScopeId,
21822182 providerKey: "google_workspace",
21832183 oauthClientId,
21842184 tokenEndpoint: "https://example.test/oauth/token",
@@ -2217,7 +2217,7 @@ describe("executor-runtime", () => { · 32 unmodified lines
22172217 const removed = yield* withExecutorApiClient({ runtime }, (client) =>
22182218 client.sources.remove({
22192219 path: {
2220− workspaceId: installation.workspaceId,
2220+ workspaceId: installation.scopeId,
22212221 sourceId: source.id,
22222222 },
22232223 }),
@@ -2238,14 +2238,14 @@ describe("executor-runtime", () => { · 14 unmodified lines
22382238 Effect.gen(function* () {
22392239 const runtime = yield* makeRuntime;
22402240 const installation = runtime.localInstallation;
2241− const oauthClientId = WorkspaceOauthClientIdSchema.make(
2241+ const oauthClientId = ScopeOauthClientIdSchema.make(
22422242 `ws_oauth_client_${crypto.randomUUID()}`,
22432243 );
22442244 const refreshSecretId = "sec_google_refresh_revoke";
22452245
2246− yield* runtime.storage.executorState.workspaceOauthClients.upsert({
2246+ yield* runtime.storage.executorState.scopeOauthClients.upsert({
22472247 id: oauthClientId,
2248− workspaceId: installation.workspaceId,
2248+ scopeId: installation.scopeId,
22492249 providerKey: "google_workspace",
22502250 label: "Local Google Workspace Client",
22512251 clientId: "google-client-id",
@@ -2267,8 +2267,8 @@ describe("executor-runtime", () => { · 15 unmodified lines
22672267 );
22682268 yield* runtime.storage.executorState.providerAuthGrants.upsert({
22692269 id: grantId,
2270− workspaceId: installation.workspaceId,
2271− actorAccountId: installation.accountId,
2270+ scopeId: installation.scopeId,
2271+ actorScopeId: installation.actorScopeId,
22722272 providerKey: "google_workspace",
22732273 oauthClientId,
22742274 tokenEndpoint: "https://example.test/oauth/token",
@@ -2324,7 +2324,7 @@ describe("executor-runtime", () => { · 49 unmodified lines
23242324 const removed = yield* withExecutorApiClient({ runtime }, (client) =>
23252325 client.sources.removeProviderAuthGrant({
23262326 path: {
2327− workspaceId: installation.workspaceId,
2327+ workspaceId: installation.scopeId,
23282328 grantId,
23292329 },
23302330 }),
@@ -2347,7 +2347,7 @@ describe("executor-runtime", () => { · 16 unmodified lines
23472347 (client) =>
23482348 client.sources.get({
23492349 path: {
2350− workspaceId: installation.workspaceId,
2350+ workspaceId: installation.scopeId,
23512351 sourceId: driveSource.id,
23522352 },
23532353 }),
@@ -2357,7 +2357,7 @@ describe("executor-runtime", () => { · 3 unmodified lines
23572357 (client) =>
23582358 client.sources.get({
23592359 path: {
2360− workspaceId: installation.workspaceId,
2360+ workspaceId: installation.scopeId,
23612361 sourceId: gmailSource.id,
23622362 },
23632363 }),
packages/platform/sdk/src/runtime/executor-state-store.ts+46−46
@@ -10,8 +10,8 @@ import type { · 9 unmodified lines
1010 ProviderAuthGrant,
1111 SecretMaterial,
1212 SourceAuthSession,
13− WorkspaceOauthClient,
14− WorkspaceSourceOauthClient,
13+ ScopeOauthClient,
14+ ScopedSourceOauthClient,
1515 } from "#schema";
1616
1717 type SecretMaterialSummary = {
@@ -25,28 +25,28 @@ type SecretMaterialSummary = { · 7 unmodified lines
2525
2626 export type ExecutorStateStoreShape = {
2727 authArtifacts: {
28− listByWorkspaceId: (
29− workspaceId: AuthArtifact["workspaceId"],
28+ listByScopeId: (
29+ scopeId: AuthArtifact["scopeId"],
3030 ) => Effect.Effect<readonly AuthArtifact[], Error, never>;
31− listByWorkspaceAndSourceId: (input: {
32− workspaceId: AuthArtifact["workspaceId"];
31+ listByScopeAndSourceId: (input: {
32+ scopeId: AuthArtifact["scopeId"];
3333 sourceId: AuthArtifact["sourceId"];
3434 }) => Effect.Effect<readonly AuthArtifact[], Error, never>;
35− getByWorkspaceSourceAndActor: (input: {
36− workspaceId: AuthArtifact["workspaceId"];
35+ getByScopeSourceAndActor: (input: {
36+ scopeId: AuthArtifact["scopeId"];
3737 sourceId: AuthArtifact["sourceId"];
38− actorAccountId: AuthArtifact["actorAccountId"];
38+ actorScopeId: AuthArtifact["actorScopeId"];
3939 slot: AuthArtifact["slot"];
4040 }) => Effect.Effect<import("effect/Option").Option<AuthArtifact>, Error, never>;
4141 upsert: (artifact: AuthArtifact) => Effect.Effect<void, Error, never>;
42− removeByWorkspaceSourceAndActor: (input: {
43− workspaceId: AuthArtifact["workspaceId"];
42+ removeByScopeSourceAndActor: (input: {
43+ scopeId: AuthArtifact["scopeId"];
4444 sourceId: AuthArtifact["sourceId"];
45− actorAccountId: AuthArtifact["actorAccountId"];
45+ actorScopeId: AuthArtifact["actorScopeId"];
4646 slot?: AuthArtifact["slot"];
4747 }) => Effect.Effect<boolean, Error, never>;
48− removeByWorkspaceAndSourceId: (input: {
49− workspaceId: AuthArtifact["workspaceId"];
48+ removeByScopeAndSourceId: (input: {
49+ scopeId: AuthArtifact["scopeId"];
5050 sourceId: AuthArtifact["sourceId"];
5151 }) => Effect.Effect<number, Error, never>;
5252 };
@@ -61,41 +61,41 @@ export type ExecutorStateStoreShape = { · 8 unmodified lines
6161 ) => Effect.Effect<boolean, Error, never>;
6262 };
6363 sourceOauthClients: {
64− getByWorkspaceSourceAndProvider: (input: {
65− workspaceId: WorkspaceSourceOauthClient["workspaceId"];
66− sourceId: WorkspaceSourceOauthClient["sourceId"];
64+ getByScopeSourceAndProvider: (input: {
65+ scopeId: ScopedSourceOauthClient["scopeId"];
66+ sourceId: ScopedSourceOauthClient["sourceId"];
6767 providerKey: string;
6868 }) => Effect.Effect<
69− import("effect/Option").Option<WorkspaceSourceOauthClient>,
69+ import("effect/Option").Option<ScopedSourceOauthClient>,
7070 Error,
7171 never
7272 >;
7373 upsert: (
74− oauthClient: WorkspaceSourceOauthClient,
74+ oauthClient: ScopedSourceOauthClient,
7575 ) => Effect.Effect<void, Error, never>;
76− removeByWorkspaceAndSourceId: (input: {
77− workspaceId: WorkspaceSourceOauthClient["workspaceId"];
78− sourceId: WorkspaceSourceOauthClient["sourceId"];
76+ removeByScopeAndSourceId: (input: {
77+ scopeId: ScopedSourceOauthClient["scopeId"];
78+ sourceId: ScopedSourceOauthClient["sourceId"];
7979 }) => Effect.Effect<number, Error, never>;
8080 };
81− workspaceOauthClients: {
82− listByWorkspaceAndProvider: (input: {
83− workspaceId: WorkspaceOauthClient["workspaceId"];
81+ scopeOauthClients: {
82+ listByScopeAndProvider: (input: {
83+ scopeId: ScopeOauthClient["scopeId"];
8484 providerKey: string;
85− }) => Effect.Effect<readonly WorkspaceOauthClient[], Error, never>;
85+ }) => Effect.Effect<readonly ScopeOauthClient[], Error, never>;
8686 getById: (
87− id: WorkspaceOauthClient["id"],
88− ) => Effect.Effect<import("effect/Option").Option<WorkspaceOauthClient>, Error, never>;
89− upsert: (oauthClient: WorkspaceOauthClient) => Effect.Effect<void, Error, never>;
90− removeById: (id: WorkspaceOauthClient["id"]) => Effect.Effect<boolean, Error, never>;
87+ id: ScopeOauthClient["id"],
88+ ) => Effect.Effect<import("effect/Option").Option<ScopeOauthClient>, Error, never>;
89+ upsert: (oauthClient: ScopeOauthClient) => Effect.Effect<void, Error, never>;
90+ removeById: (id: ScopeOauthClient["id"]) => Effect.Effect<boolean, Error, never>;
9191 };
9292 providerAuthGrants: {
93− listByWorkspaceId: (
94− workspaceId: ProviderAuthGrant["workspaceId"],
93+ listByScopeId: (
94+ scopeId: ProviderAuthGrant["scopeId"],
9595 ) => Effect.Effect<readonly ProviderAuthGrant[], Error, never>;
96− listByWorkspaceActorAndProvider: (input: {
97− workspaceId: ProviderAuthGrant["workspaceId"];
98− actorAccountId: ProviderAuthGrant["actorAccountId"];
96+ listByScopeActorAndProvider: (input: {
97+ scopeId: ProviderAuthGrant["scopeId"];
98+ actorScopeId: ProviderAuthGrant["actorScopeId"];
9999 providerKey: string;
100100 }) => Effect.Effect<readonly ProviderAuthGrant[], Error, never>;
101101 getById: (
@@ -106,8 +106,8 @@ export type ExecutorStateStoreShape = { · 4 unmodified lines
106106 };
107107 sourceAuthSessions: {
108108 listAll: () => Effect.Effect<readonly SourceAuthSession[], Error, never>;
109− listByWorkspaceId: (
110− workspaceId: SourceAuthSession["workspaceId"],
109+ listByScopeId: (
110+ scopeId: SourceAuthSession["scopeId"],
111111 ) => Effect.Effect<readonly SourceAuthSession[], Error, never>;
112112 getById: (
113113 id: SourceAuthSession["id"],
@@ -115,10 +115,10 @@ export type ExecutorStateStoreShape = { · 1 unmodified line
115115 getByState: (
116116 state: SourceAuthSession["state"],
117117 ) => Effect.Effect<import("effect/Option").Option<SourceAuthSession>, Error, never>;
118− getPendingByWorkspaceSourceAndActor: (input: {
119− workspaceId: SourceAuthSession["workspaceId"];
118+ getPendingByScopeSourceAndActor: (input: {
119+ scopeId: SourceAuthSession["scopeId"];
120120 sourceId: SourceAuthSession["sourceId"];
121− actorAccountId: SourceAuthSession["actorAccountId"];
121+ actorScopeId: SourceAuthSession["actorScopeId"];
122122 credentialSlot?: SourceAuthSession["credentialSlot"];
123123 }) => Effect.Effect<
124124 import("effect/Option").Option<SourceAuthSession>,
@@ -129,12 +129,12 @@ export type ExecutorStateStoreShape = { · 4 unmodified lines
129129 update: (
130130 id: SourceAuthSession["id"],
131131 patch: Partial<
132− Omit<SourceAuthSession, "id" | "workspaceId" | "sourceId" | "createdAt">
132+ Omit<SourceAuthSession, "id" | "scopeId" | "sourceId" | "createdAt">
133133 >,
134134 ) => Effect.Effect<import("effect/Option").Option<SourceAuthSession>, Error, never>;
135135 upsert: (session: SourceAuthSession) => Effect.Effect<void, Error, never>;
136− removeByWorkspaceAndSourceId: (
137− workspaceId: SourceAuthSession["workspaceId"],
136+ removeByScopeAndSourceId: (
137+ scopeId: SourceAuthSession["scopeId"],
138138 sourceId: SourceAuthSession["sourceId"],
139139 ) => Effect.Effect<boolean, Error, never>;
140140 };
@@ -158,15 +158,15 @@ export type ExecutorStateStoreShape = { · 17 unmodified lines
158158 getById: (
159159 executionId: Execution["id"],
160160 ) => Effect.Effect<import("effect/Option").Option<Execution>, Error, never>;
161− getByWorkspaceAndId: (
162− workspaceId: Execution["workspaceId"],
161+ getByScopeAndId: (
162+ scopeId: Execution["scopeId"],
163163 executionId: Execution["id"],
164164 ) => Effect.Effect<import("effect/Option").Option<Execution>, Error, never>;
165165 insert: (execution: Execution) => Effect.Effect<void, Error, never>;
166166 update: (
167167 executionId: Execution["id"],
168168 patch: Partial<
169− Omit<Execution, "id" | "workspaceId" | "createdByAccountId" | "createdAt">
169+ Omit<Execution, "id" | "scopeId" | "createdByScopeId" | "createdAt">
170170 >,
171171 ) => Effect.Effect<import("effect/Option").Option<Execution>, Error, never>;
172172 };
packages/platform/sdk/src/runtime/index.ts+110−72
@@ -3,30 +3,56 @@ import { · 2 unmodified lines
33 makeToolInvokerFromTools,
44 type ToolMap,
55 } from "@executor/codemode-core";
6−import { clearAllMcpConnectionPools } from "@executor/source-mcp";
7−import type { LocalInstallation } from "#schema";
6+import {
7+ clearAllMcpConnectionPools,
8+} from "@executor/source-mcp";
9+import type {
10+ LocalInstallation,
11+} from "#schema";
812 import * as Effect from "effect/Effect";
913 import * as Layer from "effect/Layer";
1014 import * as ManagedRuntime from "effect/ManagedRuntime";
1115
12−import { RuntimeSourceAuthMaterialLive } from "./auth/source-auth-material";
13−import { RuntimeSourceCatalogStoreLive } from "./catalog/source/runtime";
14−import { reconcileMissingSourceCatalogArtifacts } from "./catalog/source/reconcile";
15−import { RuntimeSourceCatalogSyncLive } from "./catalog/source/sync";
16+import {
17+ RuntimeSourceAuthMaterialLive,
18+} from "./auth/source-auth-material";
19+import {
20+ RuntimeSourceCatalogStoreLive,
21+} from "./catalog/source/runtime";
22+import {
23+ reconcileMissingSourceCatalogArtifacts,
24+} from "./catalog/source/reconcile";
25+import {
26+ RuntimeSourceCatalogSyncLive,
27+} from "./catalog/source/sync";
1628 import {
1729 SourceTypeDeclarationsRefresherService,
1830 type SourceTypeDeclarationsRefresherShape,
1931 } from "./catalog/source/type-declarations";
20−import { createLiveExecutionManager, LiveExecutionManagerService } from "./execution/live";
21−import { RuntimeExecutionResolverLive } from "./execution/workspace/environment";
22−import type { CreateWorkspaceInternalToolMap, WorkspaceInternalToolContext } from "./execution/workspace/tool-invoker";
23−import type { LoadedLocalExecutorConfig } from "./workspace-config";
24−import type { LocalExecutorConfig } from "#schema";
25−import type { InstanceConfig } from "../local/contracts";
32+import {
33+ createLiveExecutionManager,
34+ LiveExecutionManagerService,
35+} from "./execution/live";
36+import {
37+ RuntimeExecutionResolverLive,
38+} from "./execution/scope/environment";
39+import type {
40+ CreateScopeInternalToolMap,
41+ ScopeInternalToolContext,
42+} from "./execution/scope/tool-invoker";
43+import type {
44+ LoadedLocalExecutorConfig,
45+} from "./scope-config";
2646 import type {
27− ExecutorWorkspaceContext,
28− ExecutorWorkspaceDescriptor,
29−} from "../workspace";
47+ LocalExecutorConfig,
48+} from "#schema";
49+import type {
50+ InstanceConfig,
51+} from "../local/contracts";
52+import type {
53+ ExecutorScopeContext,
54+ ExecutorScopeDescriptor,
55+} from "../scope";
3056 import {
3157 LocalInstanceConfigService,
3258 type DeleteSecretMaterial,
@@ -37,12 +63,14 @@ import { · 4 unmodified lines
3763 SecretMaterialUpdaterService,
3864 type StoreSecretMaterial,
3965 type UpdateSecretMaterial,
40−} from "./workspace/secret-material-providers";
41−import type { LocalSourceArtifact } from "./source-artifacts";
66+} from "./scope/secret-material-providers";
67+import type {
68+ LocalSourceArtifact,
69+} from "./source-artifacts";
4270 import {
43− type RuntimeLocalWorkspaceState,
44− RuntimeLocalWorkspaceLive,
45−} from "./workspace/runtime-context";
71+ type RuntimeLocalScopeState,
72+ RuntimeLocalScopeLive,
73+} from "./scope/runtime-context";
4674 import {
4775 type LocalToolRuntime,
4876 type LocalToolRuntimeLoaderShape,
@@ -53,14 +81,25 @@ import { · 4 unmodified lines
5381 makeLocalStorageLayer,
5482 type InstallationStoreShape,
5583 type SourceArtifactStoreShape,
56− type WorkspaceConfigStoreShape,
57− type WorkspaceStateStoreShape,
58−} from "./workspace/storage";
59−import type { LocalWorkspaceState } from "./workspace-state";
60−import { synchronizeLocalWorkspaceState } from "./workspace/workspace-sync";
61−import { ExecutorStateStore, type ExecutorStateStoreShape } from "./executor-state-store";
62−import { RuntimeSourceAuthServiceLive } from "./sources/source-auth-service";
63−import { RuntimeSourceStoreLive } from "./sources/source-store";
84+ type ScopeConfigStoreShape,
85+ type ScopeStateStoreShape,
86+} from "./scope/storage";
87+import type {
88+ LocalScopeState,
89+} from "./scope-state";
90+import {
91+ synchronizeLocalScopeState,
92+} from "./scope/scope-sync";
93+import {
94+ ExecutorStateStore,
95+ type ExecutorStateStoreShape,
96+} from "./executor-state-store";
97+import {
98+ RuntimeSourceAuthServiceLive,
99+} from "./sources/source-auth-service";
100+import {
101+ RuntimeSourceStoreLive,
102+} from "./sources/source-store";
64103
65104 export * from "./execution/state";
66105 export * from "./sources/executor-tools";
@@ -73,28 +112,28 @@ export * from "./sources/source-credential-interactions"; · 6 unmodified lines
73112 export * from "./sources/source-adapters/mcp";
74113 export * from "./sources/source-store";
75114 export * from "./executor-state-store";
76−export * from "./execution/workspace/environment";
115+export * from "./execution/scope/environment";
77116 export * from "../sources/inspection";
78117 export * from "../sources/discovery";
79118 export * from "./execution/service";
80119 export type {
81− CreateWorkspaceInternalToolMap,
82− WorkspaceInternalToolContext,
83−} from "./execution/workspace/tool-invoker";
120+ CreateScopeInternalToolMap,
121+ ScopeInternalToolContext,
122+} from "./execution/scope/tool-invoker";
84123 export {
85124 LocalInstanceConfigService,
86125 SecretMaterialDeleterService,
87126 SecretMaterialResolverService,
88127 SecretMaterialStorerService,
89128 SecretMaterialUpdaterService,
90−} from "./workspace/secret-material-providers";
129+} from "./scope/secret-material-providers";
91130 export type {
92131 DeleteSecretMaterial,
93132 ResolveInstanceConfig,
94133 ResolveSecretMaterial,
95134 StoreSecretMaterial,
96135 UpdateSecretMaterial,
97−} from "./workspace/secret-material-providers";
136+} from "./scope/secret-material-providers";
98137 export {
99138 builtInSourceAdapters,
100139 connectableSourceAdapters,
@@ -112,7 +151,7 @@ export { · 11 unmodified lines
112151
113152 export type ExecutorRuntimeOptions = {
114153 executionResolver?: ResolveExecutionEnvironment;
115− createInternalToolMap?: CreateWorkspaceInternalToolMap;
154+ createInternalToolMap?: CreateScopeInternalToolMap;
116155 resolveSecretMaterial?: ResolveSecretMaterial;
117156 getLocalServerBaseUrl?: () => string | undefined;
118157 };
@@ -134,18 +173,18 @@ export type BoundInstallationStore = { · 15 unmodified lines
134173 getOrProvision: () => Effect.Effect<LocalInstallation, Error, never>;
135174 };
136175
137−export type BoundWorkspaceConfigStore = {
176+export type BoundScopeConfigStore = {
138177 load: () => Effect.Effect<LoadedLocalExecutorConfig, Error, never>;
139178 writeProject: (
140179 config: LocalExecutorConfig,
141180 ) => Effect.Effect<void, Error, never>;
142− resolveRelativePath: WorkspaceConfigStoreShape["resolveRelativePath"];
181+ resolveRelativePath: ScopeConfigStoreShape["resolveRelativePath"];
143182 };
144183
145−export type BoundWorkspaceStateStore = {
146− load: () => Effect.Effect<LocalWorkspaceState, Error, never>;
184+export type BoundScopeStateStore = {
185+ load: () => Effect.Effect<LocalScopeState, Error, never>;
147186 write: (
148− state: LocalWorkspaceState,
187+ state: LocalScopeState,
149188 ) => Effect.Effect<void, Error, never>;
150189 };
151190
@@ -181,8 +220,8 @@ export type RuntimeInstanceConfigService = { · 29 unmodified lines
181220
182221 export type RuntimeStorageServices = {
183222 installation: BoundInstallationStore;
184− workspaceConfig: BoundWorkspaceConfigStore;
185− workspaceState: BoundWorkspaceStateStore;
223+ scopeConfig: BoundScopeConfigStore;
224+ scopeState: BoundScopeStateStore;
186225 sourceArtifacts: BoundSourceArtifactStore;
187226 executorState: ExecutorStateStoreShape;
188227 secretMaterial: RuntimeSecretMaterialServices;
@@ -190,7 +229,7 @@ export type RuntimeStorageServices = { · 1 unmodified line
190229 };
191230
192231 export type ExecutorRuntimeServices = {
193− workspace: ExecutorWorkspaceDescriptor;
232+ scope: ExecutorScopeDescriptor;
194233 storage: RuntimeStorageServices;
195234 localToolRuntimeLoader?: BoundLocalToolRuntimeLoader;
196235 sourceTypeDeclarationsRefresher?: BoundSourceTypeDeclarationsRefresher;
@@ -219,17 +258,17 @@ const toInstallationStoreShape = ( · 22 unmodified lines
219258 getOrProvision: input.getOrProvision,
220259 });
221260
222−const toWorkspaceConfigStoreShape = (
223− input: BoundWorkspaceConfigStore,
224−): WorkspaceConfigStoreShape => ({
261+const toScopeConfigStoreShape = (
262+ input: BoundScopeConfigStore,
263+): ScopeConfigStoreShape => ({
225264 load: input.load,
226265 writeProject: ({ config }) => input.writeProject(config),
227266 resolveRelativePath: input.resolveRelativePath,
228267 });
229268
230−const toWorkspaceStateStoreShape = (
231− input: BoundWorkspaceStateStore,
232−): WorkspaceStateStoreShape => ({
269+const toScopeStateStoreShape = (
270+ input: BoundScopeStateStore,
271+): ScopeStateStoreShape => ({
233272 load: input.load,
234273 write: ({ state }) => input.write(state),
235274 });
@@ -256,14 +295,14 @@ const makeInstanceConfigLayer = (input: RuntimeInstanceConfigService) => · 20 unmodified lines
256295
257296 export const createExecutorRuntimeLayer = (
258297 input: ExecutorRuntimeOptions & ExecutorRuntimeServices & {
259− localWorkspaceState: RuntimeLocalWorkspaceState;
298+ localScopeState: RuntimeLocalScopeState;
260299 liveExecutionManager: ReturnType<typeof createLiveExecutionManager>;
261300 },
262301 ) => {
263302 const storageLayer = makeLocalStorageLayer({
264303 installationStore: toInstallationStoreShape(input.storage.installation),
265− workspaceConfigStore: toWorkspaceConfigStoreShape(input.storage.workspaceConfig),
266− workspaceStateStore: toWorkspaceStateStoreShape(input.storage.workspaceState),
304+ scopeConfigStore: toScopeConfigStoreShape(input.storage.scopeConfig),
305+ scopeStateStore: toScopeStateStoreShape(input.storage.scopeState),
267306 sourceArtifactStore: toSourceArtifactStoreShape(input.storage.sourceArtifacts),
268307 });
269308 const localToolRuntimeLayer = Layer.succeed(
@@ -283,7 +322,7 @@ export const createExecutorRuntimeLayer = ( · 13 unmodified lines
283322
284323 const baseLayer = Layer.mergeAll(
285324 Layer.succeed(ExecutorStateStore, input.storage.executorState),
286− RuntimeLocalWorkspaceLive(input.localWorkspaceState),
325+ RuntimeLocalScopeLive(input.localScopeState),
287326 storageLayer,
288327 Layer.succeed(LiveExecutionManagerService, input.liveExecutionManager),
289328 sourceTypeDeclarationsRefresherLayer,
@@ -381,16 +420,18 @@ export const createExecutorRuntimeFromServices = (input: { · 91 unmodified lines
381420 .getOrProvision()
382421 .pipe(Effect.mapError(toRuntimeBootstrapError));
383422
384− const loadedLocalConfig = yield* input.services.storage.workspaceConfig
423+ const loadedLocalConfig = yield* input.services.storage.scopeConfig
385424 .load()
386425 .pipe(Effect.mapError(toRuntimeBootstrapError));
387426
388− const runtimeWorkspace: ExecutorWorkspaceContext = {
389− ...input.services.workspace,
390− workspaceId: localInstallation.workspaceId,
391− accountId: localInstallation.accountId,
427+ const runtimeWorkspace: ExecutorScopeContext = {
428+ ...input.services.scope,
429+ scopeId: localInstallation.scopeId,
430+ actorScopeId: input.services.scope.actorScopeId ?? localInstallation.actorScopeId,
431+ resolutionScopeIds:
432+ input.services.scope.resolutionScopeIds ?? localInstallation.resolutionScopeIds,
392433 };
393− const effectiveLocalConfig = yield* synchronizeLocalWorkspaceState({
434+ const effectiveLocalConfig = yield* synchronizeLocalScopeState({
394435 loadedConfig: loadedLocalConfig,
395436 })
396437 .pipe(
@@ -399,11 +440,11 @@ export const createExecutorRuntimeFromServices = (input: { · 2 unmodified lines
399440 installationStore: toInstallationStoreShape(
400441 input.services.storage.installation,
401442 ),
402− workspaceConfigStore: toWorkspaceConfigStoreShape(
403− input.services.storage.workspaceConfig,
443+ scopeConfigStore: toScopeConfigStoreShape(
444+ input.services.storage.scopeConfig,
404445 ),
405− workspaceStateStore: toWorkspaceStateStoreShape(
406− input.services.storage.workspaceState,
446+ scopeStateStore: toScopeStateStoreShape(
447+ input.services.storage.scopeState,
407448 ),
408449 sourceArtifactStore: toSourceArtifactStoreShape(
409450 input.services.storage.sourceArtifacts,
@@ -412,12 +453,9 @@ export const createExecutorRuntimeFromServices = (input: { · 2 unmodified lines
412453 ),
413454 )
414455 .pipe(Effect.mapError(toRuntimeBootstrapError));
415− const runtimeLocalWorkspaceState: RuntimeLocalWorkspaceState = {
416− workspace: runtimeWorkspace,
417− installation: {
418− workspaceId: localInstallation.workspaceId,
419− accountId: localInstallation.accountId,
420− },
456+ const runtimeLocalScopeState: RuntimeLocalScopeState = {
457+ scope: runtimeWorkspace,
458+ installation: localInstallation,
421459 loadedConfig: {
422460 ...loadedLocalConfig,
423461 config: effectiveLocalConfig,
@@ -428,14 +466,14 @@ export const createExecutorRuntimeFromServices = (input: { · 4 unmodified lines
428466 const concreteRuntimeLayer = createExecutorRuntimeLayer({
429467 ...input,
430468 ...input.services,
431− localWorkspaceState: runtimeLocalWorkspaceState,
469+ localScopeState: runtimeLocalScopeState,
432470 liveExecutionManager,
433471 });
434472 const managedRuntime = ManagedRuntime.make(concreteRuntimeLayer);
435473 yield* managedRuntime.runtimeEffect;
436474 yield* reconcileMissingSourceCatalogArtifacts({
437− workspaceId: localInstallation.workspaceId,
438− actorAccountId: localInstallation.accountId,
475+ scopeId: localInstallation.scopeId,
476+ actorScopeId: localInstallation.actorScopeId,
439477 }).pipe(
440478 Effect.provide(managedRuntime),
441479 Effect.catchAll(() => Effect.void),
packages/platform/sdk/src/runtime/policy/invocation-policy-engine.test.ts+18−12
@@ -1,10 +1,16 @@
1−import { describe, expect, it } from "@effect/vitest";
1+import {
2+ describe,
3+ expect,
4+ it,
5+} from "@effect/vitest";
26
3−import type { LocalWorkspacePolicy } from "#schema";
7+import type {
8+ LocalScopePolicy,
9+} from "#schema";
410 import {
511 PolicyIdSchema,
612 SourceIdSchema,
7− WorkspaceIdSchema,
13+ ScopeIdSchema,
814 } from "#schema";
915
1016 import {
@@ -12,7 +18,7 @@ import { · 1 unmodified line
1218 type InvocationDescriptor,
1319 } from "./invocation-policy-engine";
1420
15−const workspaceId = WorkspaceIdSchema.make("ws_policy_engine");
21+const scopeId = ScopeIdSchema.make("ws_policy_engine");
1622 const sourceId = SourceIdSchema.make("src_policy_engine");
1723
1824 const now = 1_700_000_000_000;
@@ -29,11 +35,11 @@ const baseDescriptor: InvocationDescriptor = { · 10 unmodified lines
2935 };
3036
3137 const basePolicy = (
32− patch: Partial<LocalWorkspacePolicy> = {},
33−): LocalWorkspacePolicy => ({
38+ patch: Partial<LocalScopePolicy> = {},
39+): LocalScopePolicy => ({
3440 id: PolicyIdSchema.make(`pol_${Math.random().toString(36).slice(2, 8)}`),
3541 key: "vercel-dns",
36− workspaceId,
42+ scopeId,
3743 resourcePattern: "vercel.api.dns.createRecord",
3844 effect: "allow",
3945 approvalMode: "auto",
@@ -57,7 +63,7 @@ describe("invocation-policy-engine", () => { · 17 unmodified lines
5763 args: {},
5864 policies: [],
5965 context: {
60− workspaceId,
66+ scopeId,
6167 },
6268 });
6369
@@ -70,7 +76,7 @@ describe("invocation-policy-engine", () => { · 6 unmodified lines
7076 args: { domain: "testing.executor.sh" },
7177 policies: [],
7278 context: {
73− workspaceId,
79+ scopeId,
7480 },
7581 });
7682
@@ -85,7 +91,7 @@ describe("invocation-policy-engine", () => { · 8 unmodified lines
8591 id: PolicyIdSchema.make("pol_allow"),
8692 })],
8793 context: {
88− workspaceId,
94+ scopeId,
8995 },
9096 });
9197
@@ -113,7 +119,7 @@ describe("invocation-policy-engine", () => { · 21 unmodified lines
113119 }),
114120 ],
115121 context: {
116− workspaceId,
122+ scopeId,
117123 },
118124 });
119125
@@ -130,7 +136,7 @@ describe("invocation-policy-engine", () => { · 10 unmodified lines
130136 enabled: false,
131137 })],
132138 context: {
133− workspaceId,
139+ scopeId,
134140 },
135141 });
136142
packages/platform/sdk/src/runtime/policy/invocation-policy-engine.ts+7−7
@@ -1,5 +1,5 @@
11 import type {
2− LocalWorkspacePolicy,
2+ LocalScopePolicy,
33 Source,
44 } from "#schema";
55
@@ -22,13 +22,13 @@ export type InvocationDescriptor = { · 16 unmodified lines
2222 };
2323
2424 export type InvocationPolicyContext = {
25− workspaceId: LocalWorkspacePolicy["workspaceId"];
25+ scopeId: LocalScopePolicy["scopeId"];
2626 };
2727
2828 export type InvocationAuthorizationDecision = {
2929 kind: "allow" | "deny" | "require_interaction";
3030 reason: string;
31− matchedPolicyId: LocalWorkspacePolicy["id"] | null;
31+ matchedPolicyId: LocalScopePolicy["id"] | null;
3232 };
3333
3434 const matchesGlob = (pattern: string, value: string): boolean => {
@@ -38,7 +38,7 @@ const matchesGlob = (pattern: string, value: string): boolean => { · 3 unmodified lines
3838 return new RegExp(`^${escaped}$`).test(value);
3939 };
4040
41−const policySpecificity = (policy: LocalWorkspacePolicy): number =>
41+const policySpecificity = (policy: LocalScopePolicy): number =>
4242 policy.priority + Math.max(1, policy.resourcePattern.replace(/\*/g, "").length);
4343
4444 const defaultDecisionForInvocation = (
@@ -60,7 +60,7 @@ const defaultDecisionForInvocation = ( · 15 unmodified lines
6060 };
6161
6262 const resolvePolicyDecision = (
63− policy: LocalWorkspacePolicy,
63+ policy: LocalScopePolicy,
6464 ): InvocationAuthorizationDecision => {
6565 if (policy.effect === "deny") {
6666 return {
@@ -88,13 +88,13 @@ const resolvePolicyDecision = ( · 21 unmodified lines
8888 export const evaluateInvocationPolicy = (input: {
8989 descriptor: InvocationDescriptor;
9090 args: unknown;
91− policies: ReadonlyArray<LocalWorkspacePolicy>;
91+ policies: ReadonlyArray<LocalScopePolicy>;
9292 context: InvocationPolicyContext;
9393 }): InvocationAuthorizationDecision => {
9494 const matchingPolicies = input.policies
9595 .filter((policy) =>
9696 policy.enabled
97− && policy.workspaceId === input.context.workspaceId
97+ && policy.scopeId === input.context.scopeId
9898 && matchesGlob(policy.resourcePattern, input.descriptor.toolPath))
9999 .sort((left, right) =>
100100 policySpecificity(right) - policySpecificity(left)
packages/platform/sdk/src/runtime/scope-config.tsrenamed from packages/platform/sdk/src/runtime/workspace-config.ts+3−1
@@ -1,4 +1,6 @@
1−import type { LocalExecutorConfig } from "#schema";
1+import type {
2+ LocalExecutorConfig,
3+} from "#schema";
24
35 export type LoadedExecutorConfig = {
46 config: LocalExecutorConfig | null;
packages/platform/sdk/src/runtime/scope-errors.tsrenamed from packages/platform/sdk/src/runtime/workspace-errors.ts+6−6
@@ -1,17 +1,17 @@
11 import * as Data from "effect/Data";
22
3−export class RuntimeLocalWorkspaceUnavailableError extends Data.TaggedError(
4− "RuntimeLocalWorkspaceUnavailableError",
3+export class RuntimeLocalScopeUnavailableError extends Data.TaggedError(
4+ "RuntimeLocalScopeUnavailableError",
55 )<{
66 readonly message: string;
77 }> {}
88
9−export class RuntimeLocalWorkspaceMismatchError extends Data.TaggedError(
10− "RuntimeLocalWorkspaceMismatchError",
9+export class RuntimeLocalScopeMismatchError extends Data.TaggedError(
10+ "RuntimeLocalScopeMismatchError",
1111 )<{
1212 readonly message: string;
13− readonly requestedWorkspaceId: string;
14− readonly activeWorkspaceId: string;
13+ readonly requestedScopeId: string;
14+ readonly activeScopeId: string;
1515 }> {}
1616
1717 export class LocalConfiguredSourceNotFoundError extends Data.TaggedError(
packages/platform/sdk/src/runtime/scope-state.tsrenamed from packages/platform/sdk/src/runtime/workspace-state.ts+6−6
@@ -13,13 +13,13 @@ const LocalWorkspaceSourceStateSchema = Schema.Struct({ · 12 unmodified lines
1313 updatedAt: TimestampMsSchema,
1414 });
1515
16−const LocalWorkspacePolicyStateSchema = Schema.Struct({
16+const LocalScopePolicyStateSchema = Schema.Struct({
1717 id: PolicyIdSchema,
1818 createdAt: TimestampMsSchema,
1919 updatedAt: TimestampMsSchema,
2020 });
2121
22−export const LocalWorkspaceStateSchema = Schema.Struct({
22+export const LocalScopeStateSchema = Schema.Struct({
2323 version: Schema.Literal(1),
2424 sources: Schema.Record({
2525 key: Schema.String,
@@ -27,15 +27,15 @@ export const LocalWorkspaceStateSchema = Schema.Struct({ · 1 unmodified line
2727 }),
2828 policies: Schema.Record({
2929 key: Schema.String,
30− value: LocalWorkspacePolicyStateSchema,
30+ value: LocalScopePolicyStateSchema,
3131 }),
3232 });
3333
3434 export type LocalWorkspaceSourceState = typeof LocalWorkspaceSourceStateSchema.Type;
35−export type LocalWorkspacePolicyState = typeof LocalWorkspacePolicyStateSchema.Type;
36−export type LocalWorkspaceState = typeof LocalWorkspaceStateSchema.Type;
35+export type LocalScopePolicyState = typeof LocalScopePolicyStateSchema.Type;
36+export type LocalScopeState = typeof LocalScopeStateSchema.Type;
3737
38−export const defaultLocalWorkspaceState = (): LocalWorkspaceState => ({
38+export const defaultLocalScopeState = (): LocalScopeState => ({
3939 version: 1,
4040 sources: {},
4141 policies: {},
packages/platform/sdk/src/runtime/scope/config-secrets.tsrenamed from packages/platform/sdk/src/runtime/workspace/config-secrets.ts+0−0

File renamed without changes.

packages/platform/sdk/src/runtime/scope/runtime-context.ts+85−0
@@ -0,0 +1,85 @@
1+import * as Context from "effect/Context";
2+import * as Effect from "effect/Effect";
3+import * as Layer from "effect/Layer";
4+import * as Option from "effect/Option";
5+
6+import type {
7+ ScopeId,
8+} from "#schema";
9+import type {
10+ ExecutorScopeContext,
11+} from "../../scope";
12+import type {
13+ LoadedLocalExecutorConfig,
14+} from "../scope-config";
15+import {
16+ RuntimeLocalScopeMismatchError,
17+ RuntimeLocalScopeUnavailableError,
18+} from "../scope-errors";
19+
20+export type RuntimeLocalScopeState = {
21+ scope: ExecutorScopeContext;
22+ installation: {
23+ scopeId: ScopeId;
24+ actorScopeId: ScopeId;
25+ resolutionScopeIds: ReadonlyArray<ScopeId>;
26+ };
27+ loadedConfig: LoadedLocalExecutorConfig;
28+};
29+
30+export class RuntimeLocalScopeService extends Context.Tag(
31+ "#runtime/RuntimeLocalScopeService",
32+)<RuntimeLocalScopeService, RuntimeLocalScopeState>() {}
33+
34+export const RuntimeLocalScopeLive = (
35+ runtimeLocalScope: RuntimeLocalScopeState,
36+) => Layer.succeed(RuntimeLocalScopeService, runtimeLocalScope);
37+
38+export const provideOptionalRuntimeLocalScope = <A, E, R>(
39+ effect: Effect.Effect<A, E, R>,
40+ runtimeLocalScope: RuntimeLocalScopeState | null | undefined,
41+): Effect.Effect<A, E, R> =>
42+ runtimeLocalScope === null || runtimeLocalScope === undefined
43+ ? effect
44+ : effect.pipe(Effect.provide(RuntimeLocalScopeLive(runtimeLocalScope)));
45+
46+export const getRuntimeLocalScopeOption = () =>
47+ Effect.contextWith((context) =>
48+ Context.getOption(context, RuntimeLocalScopeService),
49+ ).pipe(
50+ Effect.map((option) => (Option.isSome(option) ? option.value : null)),
51+ ) as Effect.Effect<RuntimeLocalScopeState | null, never, never>;
52+
53+export const requireRuntimeLocalScope = (
54+ scopeId?: ScopeId,
55+): Effect.Effect<
56+ RuntimeLocalScopeState,
57+ RuntimeLocalScopeUnavailableError | RuntimeLocalScopeMismatchError,
58+ never
59+> =>
60+ Effect.gen(function* () {
61+ const runtimeLocalScope = yield* getRuntimeLocalScopeOption();
62+ if (runtimeLocalScope === null) {
63+ return yield* new RuntimeLocalScopeUnavailableError({
64+ message: "Runtime local scope is unavailable",
65+ });
66+ }
67+
68+ if (
69+ scopeId !== undefined
70+ && runtimeLocalScope.installation.scopeId !== scopeId
71+ ) {
72+ return yield* new RuntimeLocalScopeMismatchError({
73+ message: `Scope ${scopeId} is not the active local scope ${runtimeLocalScope.installation.scopeId}`,
74+ requestedScopeId: scopeId,
75+ activeScopeId: runtimeLocalScope.installation.scopeId,
76+ });
77+ }
78+
79+ return runtimeLocalScope;
80+ });
81+
82+export const requireRuntimeLocalActorScopeId = (scopeId?: ScopeId) =>
83+ requireRuntimeLocalScope(scopeId).pipe(
84+ Effect.map((runtimeLocalScope) => runtimeLocalScope.installation.actorScopeId),
85+ );
packages/platform/sdk/src/runtime/scope/scope-sync.tsrenamed from packages/platform/sdk/src/runtime/workspace/workspace-sync.ts+16−10
@@ -1,8 +1,14 @@
11 import * as Effect from "effect/Effect";
22
3−import { type LoadedLocalExecutorConfig } from "../workspace-config";
4−import { WorkspaceStateStore } from "./storage";
5−import { type LocalWorkspaceState } from "../workspace-state";
3+import {
4+ type LoadedLocalExecutorConfig,
5+} from "../scope-config";
6+import {
7+ ScopeStateStore,
8+} from "./storage";
9+import {
10+ type LocalScopeState,
11+} from "../scope-state";
612
713 const trimOrNull = (value: string | null | undefined): string | null => {
814 if (value == null) {
@@ -35,10 +41,10 @@ export const derivePolicyConfigKey = ( · 26 unmodified lines
3541
3642 const pruneLocalWorkspaceState = (input: {
3743 loadedConfig: LoadedLocalExecutorConfig;
38−}): Effect.Effect<LocalWorkspaceState, Error, WorkspaceStateStore> =>
44+}): Effect.Effect<LocalScopeState, Error, ScopeStateStore> =>
3945 Effect.gen(function* () {
40− const workspaceStateStore = yield* WorkspaceStateStore;
41− const currentState = yield* workspaceStateStore.load();
46+ const scopeStateStore = yield* ScopeStateStore;
47+ const currentState = yield* scopeStateStore.load();
4248
4349 const configuredSourceIds = new Set(
4450 Object.keys(input.loadedConfig.config?.sources ?? {}),
@@ -47,7 +53,7 @@ const pruneLocalWorkspaceState = (input: { · 2 unmodified lines
4753 Object.keys(input.loadedConfig.config?.policies ?? {}),
4854 );
4955
50− const nextState: LocalWorkspaceState = {
56+ const nextState: LocalScopeState = {
5157 ...currentState,
5258 sources: Object.fromEntries(
5359 Object.entries(currentState.sources).filter(([sourceId]) =>
@@ -65,16 +71,16 @@ const pruneLocalWorkspaceState = (input: { · 11 unmodified lines
6571 return currentState;
6672 }
6773
68− yield* workspaceStateStore.write({
74+ yield* scopeStateStore.write({
6975 state: nextState,
7076 });
7177
7278 return nextState;
7379 });
7480
75−export const synchronizeLocalWorkspaceState = (input: {
81+export const synchronizeLocalScopeState = (input: {
7682 loadedConfig: LoadedLocalExecutorConfig;
77−}): Effect.Effect<LoadedLocalExecutorConfig["config"], Error, WorkspaceStateStore> =>
83+}): Effect.Effect<LoadedLocalExecutorConfig["config"], Error, ScopeStateStore> =>
7884 Effect.gen(function* () {
7985 yield* pruneLocalWorkspaceState({
8086 loadedConfig: input.loadedConfig,
packages/platform/sdk/src/runtime/scope/secret-material-providers.tsrenamed from packages/platform/sdk/src/runtime/workspace/secret-material-providers.ts+3−1
@@ -1,4 +1,6 @@
1−import type { InstanceConfig } from "../../local/contracts";
1+import type {
2+ InstanceConfig,
3+} from "../../local/contracts";
24 import * as Context from "effect/Context";
35 import * as Effect from "effect/Effect";
46
packages/platform/sdk/src/runtime/scope/storage.tsrenamed from packages/platform/sdk/src/runtime/workspace/storage.ts+43−30
@@ -1,12 +1,25 @@
1−import type { LocalInstallation, LocalExecutorConfig } from "#schema";
1+import type {
2+ LocalInstallation,
3+ LocalExecutorConfig,
4+} from "#schema";
25 import * as Context from "effect/Context";
36 import * as Layer from "effect/Layer";
47
5−import type { LoadedLocalExecutorConfig } from "../workspace-config";
6−import type { LocalSourceArtifact } from "../source-artifacts";
7−import type { LocalWorkspaceState } from "../workspace-state";
8−import type { SourceCatalogSyncResult } from "@executor/source-core";
9−import type { Source } from "#schema";
8+import type {
9+ LoadedLocalExecutorConfig,
10+} from "../scope-config";
11+import type {
12+ LocalSourceArtifact,
13+} from "../source-artifacts";
14+import type {
15+ LocalScopeState,
16+} from "../scope-state";
17+import type {
18+ SourceCatalogSyncResult,
19+} from "@executor/source-core";
20+import type {
21+ Source,
22+} from "#schema";
1023
1124 export type InstallationStoreShape = {
1225 load: () => import("effect/Effect").Effect<LocalInstallation, Error, never>;
@@ -17,28 +30,28 @@ export class InstallationStore extends Context.Tag( · 4 unmodified lines
1730 "#runtime/InstallationStore",
1831 )<InstallationStore, InstallationStoreShape>() {}
1932
20−export type WorkspaceConfigStoreShape = {
33+export type ScopeConfigStoreShape = {
2134 load: () => import("effect/Effect").Effect<LoadedLocalExecutorConfig, Error, never>;
2235 writeProject: (input: {
2336 config: LocalExecutorConfig;
2437 }) => import("effect/Effect").Effect<void, Error, never>;
25− resolveRelativePath: (input: { path: string; workspaceRoot: string }) => string;
38+ resolveRelativePath: (input: { path: string; scopeRoot: string }) => string;
2639 };
2740
28−export class WorkspaceConfigStore extends Context.Tag(
29− "#runtime/WorkspaceConfigStore",
30−)<WorkspaceConfigStore, WorkspaceConfigStoreShape>() {}
41+export class ScopeConfigStore extends Context.Tag(
42+ "#runtime/ScopeConfigStore",
43+)<ScopeConfigStore, ScopeConfigStoreShape>() {}
3144
32−export type WorkspaceStateStoreShape = {
33− load: () => import("effect/Effect").Effect<LocalWorkspaceState, Error, never>;
45+export type ScopeStateStoreShape = {
46+ load: () => import("effect/Effect").Effect<LocalScopeState, Error, never>;
3447 write: (input: {
35− state: LocalWorkspaceState;
48+ state: LocalScopeState;
3649 }) => import("effect/Effect").Effect<void, Error, never>;
3750 };
3851
39−export class WorkspaceStateStore extends Context.Tag(
40− "#runtime/WorkspaceStateStore",
41−)<WorkspaceStateStore, WorkspaceStateStoreShape>() {}
52+export class ScopeStateStore extends Context.Tag(
53+ "#runtime/ScopeStateStore",
54+)<ScopeStateStore, ScopeStateStoreShape>() {}
4255
4356 export type SourceArtifactStoreShape = {
4457 build: (input: {
@@ -63,33 +76,33 @@ export class SourceArtifactStore extends Context.Tag( · 18 unmodified lines
6376
6477 export type LocalStorageServices =
6578 | InstallationStore
66− | WorkspaceConfigStore
67− | WorkspaceStateStore
79+ | ScopeConfigStore
80+ | ScopeStateStore
6881 | SourceArtifactStore;
6982
70−export type WorkspaceStorageServices =
71− | WorkspaceConfigStore
72− | WorkspaceStateStore
83+export type ScopeStorageServices =
84+ | ScopeConfigStore
85+ | ScopeStateStore
7386 | SourceArtifactStore;
7487
75−export const makeWorkspaceStorageLayer = (input: {
76− workspaceConfigStore: WorkspaceConfigStoreShape;
77− workspaceStateStore: WorkspaceStateStoreShape;
88+export const makeScopeStorageLayer = (input: {
89+ scopeConfigStore: ScopeConfigStoreShape;
90+ scopeStateStore: ScopeStateStoreShape;
7891 sourceArtifactStore: SourceArtifactStoreShape;
7992 }) =>
8093 Layer.mergeAll(
81− Layer.succeed(WorkspaceConfigStore, input.workspaceConfigStore),
82− Layer.succeed(WorkspaceStateStore, input.workspaceStateStore),
94+ Layer.succeed(ScopeConfigStore, input.scopeConfigStore),
95+ Layer.succeed(ScopeStateStore, input.scopeStateStore),
8396 Layer.succeed(SourceArtifactStore, input.sourceArtifactStore),
8497 );
8598
8699 export const makeLocalStorageLayer = (input: {
87100 installationStore: InstallationStoreShape;
88− workspaceConfigStore: WorkspaceConfigStoreShape;
89− workspaceStateStore: WorkspaceStateStoreShape;
101+ scopeConfigStore: ScopeConfigStoreShape;
102+ scopeStateStore: ScopeStateStoreShape;
90103 sourceArtifactStore: SourceArtifactStoreShape;
91104 }) =>
92105 Layer.mergeAll(
93106 Layer.succeed(InstallationStore, input.installationStore),
94− makeWorkspaceStorageLayer(input),
107+ makeScopeStorageLayer(input),
95108 );
packages/platform/sdk/src/runtime/source-artifacts.ts+3−1
@@ -9,7 +9,9 @@ import { · 8 unmodified lines
99 import * as Option from "effect/Option";
1010 import * as Schema from "effect/Schema";
1111
12−import { decodeCatalogSnapshotV1 } from "@executor/ir/catalog";
12+import {
13+ decodeCatalogSnapshotV1,
14+} from "@executor/ir/catalog";
1315 import {
1416 CatalogSnapshotV1Schema,
1517 type CatalogSnapshotV1,
packages/platform/sdk/src/runtime/sources/executor-tools.ts+67−60
@@ -8,45 +8,50 @@ import { · 7 unmodified lines
88 type ToolPath,
99 } from "@executor/codemode-core";
1010 import {
11− type AccountId,
11+ type ScopeId,
1212 ExecutionIdSchema,
1313 ExecutionInteractionIdSchema,
1414 SourceSchema,
1515 type Source,
16− type WorkspaceId,
1716 } from "#schema";
1817 import * as Effect from "effect/Effect";
1918 import * as Cause from "effect/Cause";
2019 import * as Exit from "effect/Exit";
2120 import * as Schema from "effect/Schema";
22−import type { RuntimeLocalWorkspaceState } from "../workspace/runtime-context";
21+import type {
22+ RuntimeLocalScopeState,
23+} from "../scope/runtime-context";
2324 import {
2425 type LocalStorageServices,
2526 type InstallationStoreShape,
2627 type SourceArtifactStoreShape,
27− type WorkspaceConfigStoreShape,
28− type WorkspaceStateStoreShape,
28+ type ScopeConfigStoreShape,
29+ type ScopeStateStoreShape,
2930 makeLocalStorageLayer,
30−} from "../workspace/storage";
31−import { provideOptionalRuntimeLocalWorkspace } from "../workspace/runtime-context";
32−import { runtimeEffectError } from "../effect-errors";
31+} from "../scope/storage";
32+import {
33+ provideOptionalRuntimeLocalScope,
34+} from "../scope/runtime-context";
35+import {
36+ runtimeEffectError,
37+} from "../effect-errors";
3338
3439 /** Run an Effect as a Promise, preserving the original error (not FiberFailure). */
3540 const runEffect = async <A>(
3641 effect: Effect.Effect<A, unknown, LocalStorageServices>,
3742 storage: {
3843 installationStore: InstallationStoreShape;
39− workspaceConfigStore: WorkspaceConfigStoreShape;
40− workspaceStateStore: WorkspaceStateStoreShape;
44+ scopeConfigStore: ScopeConfigStoreShape;
45+ scopeStateStore: ScopeStateStoreShape;
4146 sourceArtifactStore: SourceArtifactStoreShape;
4247 },
43− runtimeLocalWorkspace: RuntimeLocalWorkspaceState | null = null,
48+ runtimeLocalScope: RuntimeLocalScopeState | null = null,
4449 ): Promise<A> => {
4550 const baseLayer = makeLocalStorageLayer(storage);
4651 const exit = await Effect.runPromiseExit(
47− provideOptionalRuntimeLocalWorkspace(
52+ provideOptionalRuntimeLocalScope(
4853 effect.pipe(Effect.provide(baseLayer)),
49− runtimeLocalWorkspace,
54+ runtimeLocalScope,
5055 ),
5156 );
5257 if (Exit.isSuccess(exit)) return exit.value;
@@ -69,7 +74,9 @@ import { · 16 unmodified lines
6974 executorAddableSourceAdapters,
7075 sourceAdapterRequiresInteractiveConnect,
7176 } from "./source-adapters";
72−import { decodeSourceCredentialSelectionContent } from "./source-credential-interactions";
77+import {
78+ decodeSourceCredentialSelectionContent,
79+} from "./source-credential-interactions";
7380
7481 const ExecutorSourcesAddInputSchema = Schema.standardSchemaV1(
7582 ExecutorAddSourceInputSchema,
@@ -112,7 +119,7 @@ export const EXECUTOR_SOURCES_ADD_HELP_LINES = [ · 36 unmodified lines
112119
113120 export const buildExecutorSourcesAddDescription = (): string =>
114121 [
115− "Add an MCP, OpenAPI, or GraphQL source to the current workspace.",
122+ "Add an MCP, OpenAPI, or GraphQL source to the current scope.",
116123 ...EXECUTOR_SOURCES_ADD_HELP_LINES,
117124 ].join("\n");
118125
@@ -130,29 +137,29 @@ const toSerializableValue = <A>(value: A): A => · 11 unmodified lines
130137 JSON.parse(JSON.stringify(value)) as A;
131138
132139 type ExecutorSourcesAddToolArgs =
133− | Omit<ExecutorMcpSourceInput, "workspaceId" | "actorAccountId" | "executionId" | "interactionId">
140+ | Omit<ExecutorMcpSourceInput, "scopeId" | "actorScopeId" | "executionId" | "interactionId">
134141 | Omit<
135142 ExecutorCredentialManagedSourceInput,
136− "workspaceId" | "actorAccountId" | "executionId" | "interactionId"
143+ "scopeId" | "actorScopeId" | "executionId" | "interactionId"
137144 >;
138145
139146 type ExecutorGoogleDiscoveryToolArgs = Omit<
140147 Extract<ExecutorCredentialManagedSourceInput, { service: string; version: string }>,
141− "workspaceId" | "actorAccountId" | "executionId" | "interactionId"
148+ "scopeId" | "actorScopeId" | "executionId" | "interactionId"
142149 >;
143150
144151 type ExecutorOpenApiToolArgs = Omit<
145152 Extract<ExecutorCredentialManagedSourceInput, { specUrl: string }>,
146− "workspaceId" | "actorAccountId" | "executionId" | "interactionId"
153+ "scopeId" | "actorScopeId" | "executionId" | "interactionId"
147154 >;
148155
149156 type ExecutorGraphqlToolArgs = Omit<
150157 Extract<ExecutorCredentialManagedSourceInput, { kind: "graphql" }>,
151− "workspaceId" | "actorAccountId" | "executionId" | "interactionId"
158+ "scopeId" | "actorScopeId" | "executionId" | "interactionId"
152159 >;
153160
154161 type ExecutorCredentialPromptArgs = {
155− workspaceId: WorkspaceId;
162+ scopeId: ScopeId;
156163 sourceId: Source["id"];
157164 kind: ExecutorCredentialManagedSourceInput["kind"];
158165 endpoint?: string;
@@ -166,7 +173,7 @@ type ExecutorCredentialPromptArgs = { · 7 unmodified lines
166173
167174 const isExecutorMcpToolArgs = (
168175 args: ExecutorSourcesAddToolArgs,
169−): args is Omit<ExecutorMcpSourceInput, "workspaceId" | "actorAccountId" | "executionId" | "interactionId"> =>
176+): args is Omit<ExecutorMcpSourceInput, "scopeId" | "actorScopeId" | "executionId" | "interactionId"> =>
170177 args.kind === undefined || sourceAdapterRequiresInteractiveConnect(args.kind);
171178
172179 const isExecutorCredentialManagedSourceInput = (
@@ -176,8 +183,8 @@ const isExecutorCredentialManagedSourceInput = ( · 3 unmodified lines
176183
177184 const prepareExecutorAddSourceInput = (input: {
178185 args: ExecutorSourcesAddToolArgs;
179− workspaceId: WorkspaceId;
180− accountId: AccountId;
186+ scopeId: ScopeId;
187+ actorScopeId: ScopeId;
181188 executionId: ReturnType<typeof toExecutionId>;
182189 interactionId: ReturnType<typeof ExecutionInteractionIdSchema.make>;
183190 }): ExecutorAddSourceInput => {
@@ -194,8 +201,8 @@ const prepareExecutorAddSourceInput = (input: { · 10 unmodified lines
194201 args: input.args.args ?? null,
195202 env: input.args.env ?? null,
196203 cwd: input.args.cwd ?? null,
197− workspaceId: input.workspaceId,
198− actorAccountId: input.accountId,
204+ scopeId: input.scopeId,
205+ actorScopeId: input.actorScopeId,
199206 executionId: input.executionId,
200207 interactionId: input.interactionId,
201208 } satisfies ExecutorMcpSourceInput;
@@ -205,8 +212,8 @@ const prepareExecutorAddSourceInput = (input: { · 3 unmodified lines
205212 const args = input.args as ExecutorGoogleDiscoveryToolArgs;
206213 return {
207214 ...args,
208− workspaceId: input.workspaceId,
209− actorAccountId: input.accountId,
215+ scopeId: input.scopeId,
216+ actorScopeId: input.actorScopeId,
210217 executionId: input.executionId,
211218 interactionId: input.interactionId,
212219 } satisfies Extract<ExecutorCredentialManagedSourceInput, { service: string; version: string }>;
@@ -216,8 +223,8 @@ const prepareExecutorAddSourceInput = (input: { · 3 unmodified lines
216223 const args = input.args as ExecutorOpenApiToolArgs;
217224 return {
218225 ...args,
219− workspaceId: input.workspaceId,
220− actorAccountId: input.accountId,
226+ scopeId: input.scopeId,
227+ actorScopeId: input.actorScopeId,
221228 executionId: input.executionId,
222229 interactionId: input.interactionId,
223230 } satisfies Extract<ExecutorCredentialManagedSourceInput, { specUrl: string }>;
@@ -226,8 +233,8 @@ const prepareExecutorAddSourceInput = (input: { · 2 unmodified lines
226233 const args = input.args as ExecutorGraphqlToolArgs;
227234 return {
228235 ...args,
229− workspaceId: input.workspaceId,
230− actorAccountId: input.accountId,
236+ scopeId: input.scopeId,
237+ actorScopeId: input.actorScopeId,
231238 executionId: input.executionId,
232239 interactionId: input.interactionId,
233240 } satisfies Extract<ExecutorCredentialManagedSourceInput, { kind: "graphql" }>;
@@ -235,13 +242,13 @@ const prepareExecutorAddSourceInput = (input: { · 1 unmodified line
235242
236243 const resolveLocalCredentialUrl = (input: {
237244 baseUrl: string;
238− workspaceId: WorkspaceId;
245+ scopeId: ScopeId;
239246 sourceId: Source["id"];
240247 executionId: string;
241248 interactionId: string;
242249 }): string =>
243250 new URL(
244− `/v1/workspaces/${encodeURIComponent(input.workspaceId)}/sources/${encodeURIComponent(input.sourceId)}/credentials?interactionId=${encodeURIComponent(`${input.executionId}:${input.interactionId}`)}`,
251+ `/v1/workspaces/${encodeURIComponent(input.scopeId)}/sources/${encodeURIComponent(input.sourceId)}/credentials?interactionId=${encodeURIComponent(`${input.executionId}:${input.interactionId}`)}`,
245252 input.baseUrl,
246253 ).toString();
247254
@@ -282,7 +289,7 @@ const promptForSourceCredentialSelection = (input: { · 34 unmodified lines
282289 : `Open the secure credential page to connect ${input.source.name}`,
283290 url: resolveLocalCredentialUrl({
284291 baseUrl: input.localServerBaseUrl,
285− workspaceId: input.args.workspaceId,
292+ scopeId: input.args.scopeId,
286293 sourceId: input.args.sourceId,
287294 executionId: input.executionId,
288295 interactionId: input.interactionId,
@@ -312,14 +319,14 @@ const promptForSourceCredentialSelection = (input: { · 23 unmodified lines
312319 });
313320
314321 export const createExecutorToolMap = (input: {
315− workspaceId: WorkspaceId;
316− accountId: AccountId;
322+ scopeId: ScopeId;
323+ actorScopeId: ScopeId;
317324 sourceAuthService: RuntimeSourceAuthService;
318325 installationStore: InstallationStoreShape;
319− workspaceConfigStore: WorkspaceConfigStoreShape;
320− workspaceStateStore: WorkspaceStateStoreShape;
326+ scopeConfigStore: ScopeConfigStoreShape;
327+ scopeStateStore: ScopeStateStoreShape;
321328 sourceArtifactStore: SourceArtifactStoreShape;
322− runtimeLocalWorkspace: RuntimeLocalWorkspaceState | null;
329+ runtimeLocalScope: RuntimeLocalScopeState | null;
323330 }): ToolMap => ({
324331 "executor.sources.add": toTool({
325332 tool: {
@@ -333,8 +340,8 @@ export const createExecutorToolMap = (input: { · 7 unmodified lines
333340 );
334341 const preparedArgs = prepareExecutorAddSourceInput({
335342 args,
336− workspaceId: input.workspaceId,
337− accountId: input.accountId,
343+ scopeId: input.scopeId,
344+ actorScopeId: input.actorScopeId,
338345 executionId,
339346 interactionId,
340347 });
@@ -356,11 +363,11 @@ export const createExecutorToolMap = (input: { · 15 unmodified lines
356363 ),
357364 {
358365 installationStore: input.installationStore,
359− workspaceConfigStore: input.workspaceConfigStore,
360− workspaceStateStore: input.workspaceStateStore,
366+ scopeConfigStore: input.scopeConfigStore,
367+ scopeStateStore: input.scopeStateStore,
361368 sourceArtifactStore: input.sourceArtifactStore,
362369 },
363− input.runtimeLocalWorkspace,
370+ input.runtimeLocalScope,
364371 );
365372
366373 if (result.kind === "connected") {
@@ -379,7 +386,7 @@ export const createExecutorToolMap = (input: { · 12 unmodified lines
379386 promptForSourceCredentialSelection({
380387 args: {
381388 ...pendingArgs,
382− workspaceId: input.workspaceId,
389+ scopeId: input.scopeId,
383390 sourceId: pendingResult.source.id,
384391 },
385392 credentialSlot: pendingResult.credentialSlot,
@@ -395,11 +402,11 @@ export const createExecutorToolMap = (input: { · 9 unmodified lines
395402 }),
396403 {
397404 installationStore: input.installationStore,
398− workspaceConfigStore: input.workspaceConfigStore,
399− workspaceStateStore: input.workspaceStateStore,
405+ scopeConfigStore: input.scopeConfigStore,
406+ scopeStateStore: input.scopeStateStore,
400407 sourceArtifactStore: input.sourceArtifactStore,
401408 },
402− input.runtimeLocalWorkspace,
409+ input.runtimeLocalScope,
403410 );
404411
405412 pendingArgs = pendingResult.credentialSlot === "import"
@@ -431,11 +438,11 @@ export const createExecutorToolMap = (input: { · 25 unmodified lines
431438 ),
432439 {
433440 installationStore: input.installationStore,
434− workspaceConfigStore: input.workspaceConfigStore,
435− workspaceStateStore: input.workspaceStateStore,
441+ scopeConfigStore: input.scopeConfigStore,
442+ scopeStateStore: input.scopeStateStore,
436443 sourceArtifactStore: input.sourceArtifactStore,
437444 },
438− input.runtimeLocalWorkspace,
445+ input.runtimeLocalScope,
439446 );
440447
441448 if (completed.kind === "connected") {
@@ -481,11 +488,11 @@ export const createExecutorToolMap = (input: { · 39 unmodified lines
481488 }),
482489 {
483490 installationStore: input.installationStore,
484− workspaceConfigStore: input.workspaceConfigStore,
485− workspaceStateStore: input.workspaceStateStore,
491+ scopeConfigStore: input.scopeConfigStore,
492+ scopeStateStore: input.scopeStateStore,
486493 sourceArtifactStore: input.sourceArtifactStore,
487494 },
488− input.runtimeLocalWorkspace,
495+ input.runtimeLocalScope,
489496 );
490497
491498 if (response.action !== "accept") {
@@ -494,17 +501,17 @@ export const createExecutorToolMap = (input: { · 2 unmodified lines
494501
495502 const connected = await runEffect(
496503 input.sourceAuthService.getSourceById({
497− workspaceId: input.workspaceId,
504+ scopeId: input.scopeId,
498505 sourceId: result.source.id,
499− actorAccountId: input.accountId,
506+ actorScopeId: input.actorScopeId,
500507 }),
501508 {
502509 installationStore: input.installationStore,
503− workspaceConfigStore: input.workspaceConfigStore,
504− workspaceStateStore: input.workspaceStateStore,
510+ scopeConfigStore: input.scopeConfigStore,
511+ scopeStateStore: input.scopeStateStore,
505512 sourceArtifactStore: input.sourceArtifactStore,
506513 },
507− input.runtimeLocalWorkspace,
514+ input.runtimeLocalScope,
508515 );
509516 return toSerializableValue(connected);
510517 },
packages/platform/sdk/src/runtime/sources/graphql-tools.test.ts+13−3
@@ -1,8 +1,18 @@
1−import { createServer, type IncomingMessage } from "node:http";
1+import {
2+ createServer,
3+ type IncomingMessage,
4+} from "node:http";
25
3−import { afterEach, describe, expect, it } from "@effect/vitest";
6+import {
7+ afterEach,
8+ describe,
9+ expect,
10+ it,
11+} from "@effect/vitest";
412
5−import { createGraphqlToolFromPersistedOperation } from "./graphql-tools";
13+import {
14+ createGraphqlToolFromPersistedOperation,
15+} from "./graphql-tools";
616
717 type CapturedRequest = {
818 path: string;
packages/platform/sdk/src/runtime/sources/source-adapter-fixture-matrix.test.ts+47−23
@@ -1,5 +1,9 @@
1−import { createServer } from "node:http";
2−import { fileURLToPath } from "node:url";
1+import {
2+ createServer,
3+} from "node:http";
4+import {
5+ fileURLToPath,
6+} from "node:url";
37
48 import {
59 FileSystem,
@@ -9,7 +13,9 @@ import { · 3 unmodified lines
913 HttpApiSchema,
1014 OpenApi,
1115 } from "@effect/platform";
12−import { NodeFileSystem } from "@effect/platform-node";
16+import {
17+ NodeFileSystem,
18+} from "@effect/platform-node";
1319 import {
1420 buildOpenApiToolPresentation,
1521 compileOpenApiToolDefinitions,
@@ -21,7 +27,11 @@ import { · 5 unmodified lines
2127 compileGoogleDiscoveryToolDefinitions,
2228 extractGoogleDiscoveryManifest,
2329 } from "@executor/source-google-discovery";
24−import { describe, expect, it } from "@effect/vitest";
30+import {
31+ describe,
32+ expect,
33+ it,
34+} from "@effect/vitest";
2535
2636 import type {
2737 Source,
@@ -32,13 +42,19 @@ import { · 4 unmodified lines
3242 SourceCatalogIdSchema,
3343 SourceCatalogRevisionIdSchema,
3444 SourceIdSchema,
35− WorkspaceIdSchema,
45+ ScopeIdSchema,
3646 } from "#schema";
3747 import * as Effect from "effect/Effect";
38−import { Schema } from "effect";
48+import {
49+ Schema,
50+} from "effect";
3951
40−import { projectCatalogForAgentSdk } from "@executor/ir/catalog";
41−import type { CatalogSnapshotV1 } from "@executor/ir/model";
52+import {
53+ projectCatalogForAgentSdk,
54+} from "@executor/ir/catalog";
55+import type {
56+ CatalogSnapshotV1,
57+} from "@executor/ir/model";
4258 import {
4359 createCatalogTypeProjector,
4460 projectedCatalogTypeRoots,
@@ -52,12 +68,20 @@ import { · 7 unmodified lines
5268 expandCatalogToolByPath,
5369 type LoadedSourceCatalog,
5470 } from "../catalog/source/runtime";
55−import { invokeIrTool } from "../execution/ir-execution";
56−import { createGoogleDiscoveryCatalogSnapshot } from "@executor/source-google-discovery";
57−import { createGraphqlCatalogSnapshot } from "@executor/source-graphql";
58−import { createOpenApiCatalogSnapshot } from "@executor/source-openapi";
71+import {
72+ invokeIrTool,
73+} from "../execution/ir-execution";
74+import {
75+ createGoogleDiscoveryCatalogSnapshot,
76+} from "@executor/source-google-discovery";
77+import {
78+ createGraphqlCatalogSnapshot,
79+} from "@executor/source-graphql";
80+import {
81+ createOpenApiCatalogSnapshot,
82+} from "@executor/source-openapi";
5983
60−const FIXTURE_WORKSPACE_ID = WorkspaceIdSchema.make("ws_source_fixture_matrix");
84+const FIXTURE_WORKSPACE_ID = ScopeIdSchema.make("ws_source_fixture_matrix");
6185
6286 const readFixture = (name: string) =>
6387 FileSystem.FileSystem.pipe(
@@ -79,7 +103,7 @@ const makeSource = (input: { · 15 unmodified lines
79103 binding: Source["binding"];
80104 }): Source => ({
81105 id: SourceIdSchema.make(input.id),
82− workspaceId: FIXTURE_WORKSPACE_ID,
106+ scopeId: FIXTURE_WORKSPACE_ID,
83107 name: input.name,
84108 kind: input.kind,
85109 endpoint: input.endpoint,
@@ -107,7 +131,7 @@ const makeLoadedCatalog = (input: { · 21 unmodified lines
107131 );
108132 const sourceRecord = {
109133 id: input.source.id,
110− workspaceId: input.source.workspaceId,
134+ scopeId: input.source.scopeId,
111135 catalogId,
112136 catalogRevisionId: revisionId,
113137 name: input.source.name,
@@ -725,8 +749,8 @@ describe("source adapter fixture matrix", () => { · 611 unmodified lines
725749
726750 const getItemResult = await Effect.runPromise(
727751 invokeIrTool({
728− workspaceId: source.workspaceId,
729− accountId: "acct_fixture" as any,
752+ scopeId: source.scopeId,
753+ actorScopeId: "acct_fixture" as any,
730754 tool: getItemTool,
731755 auth,
732756 args: {
@@ -741,8 +765,8 @@ describe("source adapter fixture matrix", () => { · 8 unmodified lines
741765 );
742766 const submitFormResult = await Effect.runPromise(
743767 invokeIrTool({
744− workspaceId: source.workspaceId,
745− accountId: "acct_fixture" as any,
768+ scopeId: source.scopeId,
769+ actorScopeId: "acct_fixture" as any,
746770 tool: submitFormTool,
747771 auth,
748772 args: {
@@ -898,8 +922,8 @@ describe("source adapter fixture matrix", () => { · 149 unmodified lines
898922
899923 const result = await Effect.runPromise(
900924 invokeIrTool({
901− workspaceId: source.workspaceId,
902− accountId: "acct_fixture" as any,
925+ scopeId: source.scopeId,
926+ actorScopeId: "acct_fixture" as any,
903927 tool,
904928 auth,
905929 args: {
@@ -1332,8 +1356,8 @@ describe("source adapter fixture matrix", () => { · 426 unmodified lines
13321356 } as const;
13331357
13341358 const result = yield* invokeIrTool({
1335− workspaceId: source.workspaceId,
1336− accountId: "acct_fixture" as any,
1359+ scopeId: source.scopeId,
1360+ actorScopeId: "acct_fixture" as any,
13371361 tool,
13381362 auth,
13391363 args: {},
packages/platform/sdk/src/runtime/sources/source-adapters/google-discovery.test.ts+24−8
@@ -1,14 +1,30 @@
1−import { createServer } from "node:http";
1+import {
2+ createServer,
3+} from "node:http";
24
3−import { describe, expect, it } from "@effect/vitest";
4−import { SourceIdSchema } from "#schema";
5+import {
6+ describe,
7+ expect,
8+ it,
9+} from "@effect/vitest";
10+import {
11+ SourceIdSchema,
12+} from "#schema";
513
614 import * as Effect from "effect/Effect";
715
8−import { googleDiscoverySourceAdapter } from "./google-discovery";
9−import { snapshotFromSourceCatalogSyncResult } from "../catalog-sync-result";
10−import { createSourceFromPayload } from "../source-definitions";
11−import { runtimeEffectError } from "../../effect-errors";
16+import {
17+ googleDiscoverySourceAdapter,
18+} from "./google-discovery";
19+import {
20+ snapshotFromSourceCatalogSyncResult,
21+} from "../catalog-sync-result";
22+import {
23+ createSourceFromPayload,
24+} from "../source-definitions";
25+import {
26+ runtimeEffectError,
27+} from "../../effect-errors";
1228
1329 const fetchLiveDiscoveryDocument = async (): Promise<string> => {
1430 const response = await fetch("https://www.googleapis.com/discovery/v1/apis/sheets/v4/rest", {
@@ -66,7 +82,7 @@ describe("google discovery source adapter", () => { · 51 unmodified lines
6682 await withStaticServer(discoveryDocument, async (discoveryUrl) => {
6783 const source = await Effect.runPromise(
6884 createSourceFromPayload({
69− workspaceId: "ws_test" as any,
85+ scopeId: "ws_test" as any,
7086 sourceId: SourceIdSchema.make(`src_${crypto.randomUUID()}`),
7187 payload: {
7288 name: "Google Sheets",
packages/platform/sdk/src/runtime/sources/source-adapters/graphql.test.ts+24−8
@@ -1,13 +1,29 @@
1−import { createServer } from "node:http";
1+import {
2+ createServer,
3+} from "node:http";
24
3−import { describe, expect, it } from "@effect/vitest";
4−import { SourceIdSchema } from "#schema";
5+import {
6+ describe,
7+ expect,
8+ it,
9+} from "@effect/vitest";
10+import {
11+ SourceIdSchema,
12+} from "#schema";
513 import * as Effect from "effect/Effect";
6−import { vi } from "vitest";
14+import {
15+ vi,
16+} from "vitest";
717
8−import { createSourceFromPayload } from "../source-definitions";
9−import { graphqlSourceAdapter } from "./graphql";
10−import { runtimeEffectError } from "../../effect-errors";
18+import {
19+ createSourceFromPayload,
20+} from "../source-definitions";
21+import {
22+ graphqlSourceAdapter,
23+} from "./graphql";
24+import {
25+ runtimeEffectError,
26+} from "../../effect-errors";
1127
1228 const toError = (cause: unknown): Error =>
1329 cause instanceof Error ? cause : new Error(String(cause));
@@ -92,7 +108,7 @@ describe("graphql source adapter", () => { · 78 unmodified lines
92108 );
93109
94110 const source = yield* createSourceFromPayload({
95− workspaceId: "ws_test" as any,
111+ scopeId: "ws_test" as any,
96112 sourceId: SourceIdSchema.make(`src_${crypto.randomUUID()}`),
97113 payload: {
98114 name: "GraphQL Timeout",
packages/platform/sdk/src/runtime/sources/source-adapters/index.ts+6−2
@@ -4,9 +4,13 @@ import { · 3 unmodified lines
44 createSourceAdapterComposition,
55 type SourceAdapter,
66 } from "@executor/source-core";
7−import { externalSourceAdapters } from "@executor/source-builtins";
7+import {
8+ externalSourceAdapters,
9+} from "@executor/source-builtins";
810
9−import { internalSourceAdapter } from "./internal";
11+import {
12+ internalSourceAdapter,
13+} from "./internal";
1014
1115 export type * from "@executor/source-core";
1216
packages/platform/sdk/src/runtime/sources/source-adapters/internal.ts+3−1
@@ -10,7 +10,9 @@ import { · 9 unmodified lines
1010 type Source,
1111 type SourceAdapter,
1212 } from "@executor/source-core";
13−import { runtimeEffectError } from "../../effect-errors";
13+import {
14+ runtimeEffectError,
15+} from "../../effect-errors";
1416
1517 const InternalBindingConfigSchema = Schema.Struct({});
1618
packages/platform/sdk/src/runtime/sources/source-adapters/mcp.test.ts+73−34
@@ -1,10 +1,24 @@
1−import { randomUUID } from "node:crypto";
1+import {
2+ randomUUID,
3+} from "node:crypto";
24
3−import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";
4−import type { OAuthClientProvider } from "@modelcontextprotocol/sdk/client/auth.js";
5−import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
6−import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
7−import { describe, expect, it } from "@effect/vitest";
5+import {
6+ createMcpExpressApp,
7+} from "@modelcontextprotocol/sdk/server/express.js";
8+import type {
9+ OAuthClientProvider,
10+} from "@modelcontextprotocol/sdk/client/auth.js";
11+import {
12+ McpServer,
13+} from "@modelcontextprotocol/sdk/server/mcp.js";
14+import {
15+ StreamableHTTPServerTransport,
16+} from "@modelcontextprotocol/sdk/server/streamableHttp.js";
17+import {
18+ describe,
19+ expect,
20+ it,
21+} from "@effect/vitest";
822 import {
923 SourceCatalogIdSchema,
1024 SourceCatalogRevisionIdSchema,
@@ -17,20 +31,39 @@ import type { · 6 unmodified lines
1731 } from "#schema";
1832
1933 import * as Effect from "effect/Effect";
20−import { z } from "zod/v4";
34+import {
35+ z,
36+} from "zod/v4";
2137
22−import { projectCatalogForAgentSdk } from "@executor/ir/catalog";
23−import type { CatalogSnapshotV1 } from "@executor/ir/model";
24−import { createCatalogTypeProjector, projectedCatalogTypeRoots } from "../../catalog/catalog-typescript";
25−import { invokeIrTool } from "../../execution/ir-execution";
38+import {
39+ projectCatalogForAgentSdk,
40+} from "@executor/ir/catalog";
41+import type {
42+ CatalogSnapshotV1,
43+} from "@executor/ir/model";
44+import {
45+ createCatalogTypeProjector,
46+ projectedCatalogTypeRoots,
47+} from "../../catalog/catalog-typescript";
48+import {
49+ invokeIrTool,
50+} from "../../execution/ir-execution";
2651 import {
2752 expandCatalogToolByPath,
2853 type LoadedSourceCatalog,
2954 } from "../../catalog/source/runtime";
30−import { snapshotFromSourceCatalogSyncResult } from "../catalog-sync-result";
31−import { createSourceFromPayload } from "../source-definitions";
32−import { mcpSourceAdapter } from "./mcp";
33−import { runtimeEffectError } from "../../effect-errors";
55+import {
56+ snapshotFromSourceCatalogSyncResult,
57+} from "../catalog-sync-result";
58+import {
59+ createSourceFromPayload,
60+} from "../source-definitions";
61+import {
62+ mcpSourceAdapter,
63+} from "./mcp";
64+import {
65+ runtimeEffectError,
66+} from "../../effect-errors";
3467
3568 type RealMcpServer = {
3669 endpoint: string;
@@ -47,7 +80,7 @@ const makeLoadedCatalog = (input: { · 10 unmodified lines
4780 );
4881 const sourceRecord = {
4982 id: input.source.id,
50− workspaceId: input.source.workspaceId,
83+ scopeId: input.source.scopeId,
5184 catalogId,
5285 catalogRevisionId: revisionId,
5386 name: input.source.name,
@@ -535,9 +568,15 @@ const makeStatefulMcpServer = Effect.acquireRelease( · 481 unmodified lines
535568 );
536569
537570 const STDIO_TEST_SERVER_SCRIPT = `
538−import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
539−import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
540−import { z } from "zod/v4";
571+import {
572+ McpServer,
573+} from "@modelcontextprotocol/sdk/server/mcp.js";
574+import {
575+ StdioServerTransport,
576+} from "@modelcontextprotocol/sdk/server/stdio.js";
577+import {
578+ z,
579+} from "zod/v4";
541580
542581 const server = new McpServer(
543582 { name: "mcp-stdio-test-server", version: "1.0.0" },
@@ -594,7 +633,7 @@ describe("mcp source adapter", () => { · 50 unmodified lines
594633 Effect.gen(function* () {
595634 const realServer = yield* makeRealMcpServer;
596635 const source = yield* createSourceFromPayload({
597− workspaceId: "ws_test" as any,
636+ scopeId: "ws_test" as any,
598637 sourceId: SourceIdSchema.make(`src_${randomUUID()}`),
599638 payload: {
600639 name: "MCP Demo",
@@ -682,7 +721,7 @@ describe("mcp source adapter", () => { · 81 unmodified lines
682721 Effect.gen(function* () {
683722 const realServer = yield* makeRealMcpServer;
684723 const source = yield* createSourceFromPayload({
685− workspaceId: "ws_test" as any,
724+ scopeId: "ws_test" as any,
686725 sourceId: SourceIdSchema.make(`src_${randomUUID()}`),
687726 payload: {
688727 name: "MCP Demo",
@@ -733,8 +772,8 @@ describe("mcp source adapter", () => { · 44 unmodified lines
733772 }
734773
735774 const result = yield* invokeIrTool({
736− workspaceId: source.workspaceId,
737− accountId: "acct_test" as any,
775+ scopeId: source.scopeId,
776+ actorScopeId: "acct_test" as any,
738777 tool,
739778 auth: {
740779 placements: [],
@@ -769,7 +808,7 @@ describe("mcp source adapter", () => { · 28 unmodified lines
769808 const realServer = yield* makeAuthenticatedMcpServer("Bearer mcp-auth-token");
770809 const authProvider = makeStaticAuthProvider("mcp-auth-token");
771810 const source = yield* createSourceFromPayload({
772− workspaceId: "ws_test" as any,
811+ scopeId: "ws_test" as any,
773812 sourceId: SourceIdSchema.make(`src_${randomUUID()}`),
774813 payload: {
775814 name: "Authenticated MCP Demo",
@@ -822,8 +861,8 @@ describe("mcp source adapter", () => { · 46 unmodified lines
822861 }
823862
824863 const result = yield* invokeIrTool({
825− workspaceId: source.workspaceId,
826− accountId: "acct_test" as any,
864+ scopeId: source.scopeId,
865+ actorScopeId: "acct_test" as any,
827866 tool,
828867 auth: resolvedAuth,
829868 args: {
@@ -849,7 +888,7 @@ describe("mcp source adapter", () => { · 19 unmodified lines
849888 Effect.gen(function* () {
850889 const realServer = yield* makeStatefulMcpServer;
851890 const source = yield* createSourceFromPayload({
852− workspaceId: "ws_test" as any,
891+ scopeId: "ws_test" as any,
853892 sourceId: SourceIdSchema.make(`src_${randomUUID()}`),
854893 payload: {
855894 name: "Stateful MCP Demo",
@@ -905,8 +944,8 @@ describe("mcp source adapter", () => { · 49 unmodified lines
905944 }
906945
907946 const incrementResult = yield* invokeIrTool({
908− workspaceId: source.workspaceId,
909− accountId: "acct_test" as any,
947+ scopeId: source.scopeId,
948+ actorScopeId: "acct_test" as any,
910949 tool: incrementTool,
911950 auth: {
912951 placements: [],
@@ -924,8 +963,8 @@ describe("mcp source adapter", () => { · 11 unmodified lines
924963 });
925964
926965 const readResult = yield* invokeIrTool({
927− workspaceId: source.workspaceId,
928− accountId: "acct_test" as any,
966+ scopeId: source.scopeId,
967+ actorScopeId: "acct_test" as any,
929968 tool: readTool,
930969 auth: {
931970 placements: [],
@@ -970,7 +1009,7 @@ describe("mcp source adapter", () => { · 38 unmodified lines
9701009 it.scoped("supports stdio MCP sources", () =>
9711010 Effect.gen(function* () {
9721011 const source = yield* createSourceFromPayload({
973− workspaceId: "ws_test" as any,
1012+ scopeId: "ws_test" as any,
9741013 sourceId: SourceIdSchema.make(`src_${randomUUID()}`),
9751014 payload: {
9761015 name: "Stdio MCP Demo",
@@ -1025,8 +1064,8 @@ describe("mcp source adapter", () => { · 48 unmodified lines
10251064 }
10261065
10271066 const result = yield* invokeIrTool({
1028− workspaceId: source.workspaceId,
1029− accountId: "acct_test" as any,
1067+ scopeId: source.scopeId,
1068+ actorScopeId: "acct_test" as any,
10301069 tool,
10311070 auth: {
10321071 placements: [],
packages/platform/sdk/src/runtime/sources/source-auth-service.ts+379−305
@@ -9,7 +9,6 @@ import { · 8 unmodified lines
99 type SourceAdapterOauth2SetupConfig,
1010 } from "@executor/source-core";
1111 import {
12− AccountId,
1312 type CredentialSlot,
1413 McpSourceAuthSessionDataJsonSchema,
1514 type McpSourceAuthSessionData,
@@ -30,12 +29,12 @@ import { · 14 unmodified lines
3029 SourceSchema,
3130 type SecretRef,
3231 type StringMap,
33− WorkspaceOauthClientIdSchema,
34− WorkspaceSourceOauthClientIdSchema,
35− WorkspaceSourceOauthClientMetadataJsonSchema,
36− type WorkspaceOauthClient,
37− type WorkspaceSourceOauthClientRedirectMode,
38− type WorkspaceId,
32+ ScopeOauthClientIdSchema,
33+ ScopedSourceOauthClientIdSchema,
34+ ScopedSourceOauthClientMetadataJsonSchema,
35+ type ScopeOauthClient,
36+ type ScopedSourceOauthClientRedirectMode,
37+ type ScopeId,
3938 } from "#schema";
4039 import * as Context from "effect/Context";
4140 import * as Either from "effect/Either";
@@ -51,15 +50,17 @@ import { · 9 unmodified lines
5150 type LiveExecutionManager,
5251 } from "../execution/live";
5352 import {
54− getRuntimeLocalWorkspaceOption,
55− provideOptionalRuntimeLocalWorkspace,
56− type RuntimeLocalWorkspaceState,
57−} from "../workspace/runtime-context";
53+ getRuntimeLocalScopeOption,
54+ provideOptionalRuntimeLocalScope,
55+ type RuntimeLocalScopeState,
56+} from "../scope/runtime-context";
5857 import {
5958 exchangeMcpOAuthAuthorizationCode,
6059 startMcpOAuthAuthorization,
6160 } from "../auth/mcp-oauth";
62−import { createPersistedMcpOAuthSourceAuth } from "../auth/mcp-auth-provider";
61+import {
62+ createPersistedMcpOAuthSourceAuth,
63+} from "../auth/mcp-auth-provider";
6364 import {
6465 createSourceFromPayload,
6566 updateSourceFromPayload,
@@ -79,7 +80,7 @@ import { · 13 unmodified lines
7980 SecretMaterialResolverService,
8081 SecretMaterialStorerService,
8182 type StoreSecretMaterial,
82−} from "../workspace/secret-material-providers";
83+} from "../scope/secret-material-providers";
8384 import {
8485 removeAuthLeaseAndSecrets,
8586 upsertOauth2AuthorizedUserLeaseFromTokenResponse,
@@ -97,14 +98,23 @@ import { · 11 unmodified lines
9798 createPkceCodeVerifier,
9899 exchangeOAuth2AuthorizationCode,
99100 } from "../auth/oauth2-pkce";
100−import { startOauthLoopbackRedirectServer } from "../auth/oauth-loopback";
101+import {
102+ startOauthLoopbackRedirectServer,
103+} from "../auth/oauth-loopback";
101104 import {
102105 type RuntimeSourceStore,
103106 RuntimeSourceStoreService,
104107 } from "./source-store";
105−import type { WorkspaceStorageServices } from "../workspace/storage";
106−import { ExecutorStateStore, type ExecutorStateStoreShape } from "../executor-state-store";
107−import { runtimeEffectError } from "../effect-errors";
108+import type {
109+ ScopeStorageServices,
110+} from "../scope/storage";
111+import {
112+ ExecutorStateStore,
113+ type ExecutorStateStoreShape,
114+} from "../executor-state-store";
115+import {
116+ runtimeEffectError,
117+} from "../effect-errors";
108118
109119 const trimOrNull = (value: string | null | undefined): string | null => {
110120 if (value === null || value === undefined) {
@@ -199,11 +209,11 @@ const normalizeMcpEndpoint = (input: { · 88 unmodified lines
199209
200210 const resolveSourceCredentialOauthCompleteUrl = (input: {
201211 baseUrl: string;
202− workspaceId: WorkspaceId;
212+ scopeId: ScopeId;
203213 sourceId: Source["id"];
204214 }): string =>
205215 new URL(
206− `/v1/workspaces/${encodeURIComponent(input.workspaceId)}/sources/${encodeURIComponent(input.sourceId)}/credentials/oauth/complete`,
216+ `/v1/workspaces/${encodeURIComponent(input.scopeId)}/sources/${encodeURIComponent(input.sourceId)}/credentials/oauth/complete`,
207217 input.baseUrl,
208218 ).toString();
209219
@@ -217,10 +227,10 @@ const resolveSourceOAuthCallbackUrl = (input: { · 7 unmodified lines
217227
218228 const resolveWorkspaceProviderOauthCompleteUrl = (input: {
219229 baseUrl: string;
220− workspaceId: WorkspaceId;
230+ scopeId: ScopeId;
221231 }): string =>
222232 new URL(
223− `/v1/workspaces/${encodeURIComponent(input.workspaceId)}/oauth/provider/callback`,
233+ `/v1/workspaces/${encodeURIComponent(input.scopeId)}/oauth/provider/callback`,
224234 input.baseUrl,
225235 ).toString();
226236
@@ -491,7 +501,7 @@ const serializeJson = (value: unknown): string | null => { · 264 unmodified lines
491501 };
492502
493503 const updateSourceStatus = Effect.fn("source.status.update")((sourceStore: RuntimeSourceStore, source: Source, input: {
494− actorAccountId?: AccountId | null;
504+ actorScopeId?: ScopeId | null;
495505 status: Source["status"];
496506 lastError?: string | null;
497507 auth?: Source["auth"];
@@ -499,9 +509,9 @@ const updateSourceStatus = Effect.fn("source.status.update")((sourceStore: Runti · 1 unmodified line
499509 }) =>
500510 Effect.gen(function* () {
501511 const latest = yield* sourceStore.loadSourceById({
502− workspaceId: source.workspaceId,
512+ scopeId: source.scopeId,
503513 sourceId: source.id,
504− actorAccountId: input.actorAccountId,
514+ actorScopeId: input.actorScopeId,
505515 });
506516
507517 return yield* sourceStore.persistSource({
@@ -512,7 +522,7 @@ const updateSourceStatus = Effect.fn("source.status.update")((sourceStore: Runti · 4 unmodified lines
512522 importAuth: input.importAuth ?? latest.importAuth,
513523 updatedAt: Date.now(),
514524 }, {
515− actorAccountId: input.actorAccountId,
525+ actorScopeId: input.actorScopeId,
516526 });
517527 }).pipe(
518528 Effect.withSpan("source.status.update", {
@@ -564,8 +574,8 @@ export type ExecutorHttpSourceAuthInput = · 45 unmodified lines
564574 export type ExecutorAddSourceInput =
565575 | {
566576 kind?: "mcp";
567− workspaceId: WorkspaceId;
568− actorAccountId?: AccountId | null;
577+ scopeId: ScopeId;
578+ actorScopeId?: ScopeId | null;
569579 executionId: SourceAuthSession["executionId"];
570580 interactionId: SourceAuthSession["interactionId"];
571581 endpoint?: string | null;
@@ -581,8 +591,8 @@ export type ExecutorAddSourceInput = · 9 unmodified lines
581591 }
582592 | {
583593 kind: "openapi";
584− workspaceId: WorkspaceId;
585− actorAccountId?: AccountId | null;
594+ scopeId: ScopeId;
595+ actorScopeId?: ScopeId | null;
586596 executionId: SourceAuthSession["executionId"];
587597 interactionId: SourceAuthSession["interactionId"];
588598 endpoint: string;
@@ -595,8 +605,8 @@ export type ExecutorAddSourceInput = · 6 unmodified lines
595605 }
596606 | {
597607 kind: "graphql";
598− workspaceId: WorkspaceId;
599− actorAccountId?: AccountId | null;
608+ scopeId: ScopeId;
609+ actorScopeId?: ScopeId | null;
600610 executionId: SourceAuthSession["executionId"];
601611 interactionId: SourceAuthSession["interactionId"];
602612 endpoint: string;
@@ -608,15 +618,15 @@ export type ExecutorAddSourceInput = · 5 unmodified lines
608618 }
609619 | {
610620 kind: "google_discovery";
611− workspaceId: WorkspaceId;
612− actorAccountId?: AccountId | null;
621+ scopeId: ScopeId;
622+ actorScopeId?: ScopeId | null;
613623 executionId: SourceAuthSession["executionId"];
614624 interactionId: SourceAuthSession["interactionId"];
615625 service: string;
616626 version: string;
617627 discoveryUrl?: string | null;
618628 scopes?: ReadonlyArray<string> | null;
619− workspaceOauthClientId?: WorkspaceOauthClient["id"] | null;
629+ scopeOauthClientId?: ScopeOauthClient["id"] | null;
620630 oauthClient?: SourceOauthClientInput | null;
621631 name?: string | null;
622632 namespace?: string | null;
@@ -641,8 +651,8 @@ export type ExecutorMcpSourceInput = Exclude< · 18 unmodified lines
641651 >;
642652
643653 export type ConnectMcpSourceInput = {
644− workspaceId: WorkspaceId;
645− actorAccountId?: AccountId | null;
654+ scopeId: ScopeId;
655+ actorScopeId?: ScopeId | null;
646656 sourceId?: Source["id"] | null;
647657 endpoint?: string | null;
648658 name?: string | null;
@@ -659,11 +669,11 @@ export type ConnectMcpSourceInput = { · 10 unmodified lines
659669 };
660670
661671 export type ConnectGoogleDiscoveryBatchInput = {
662− workspaceId: WorkspaceId;
663− actorAccountId?: AccountId | null;
672+ scopeId: ScopeId;
673+ actorScopeId?: ScopeId | null;
664674 executionId: SourceAuthSession["executionId"];
665675 interactionId: SourceAuthSession["interactionId"];
666− workspaceOauthClientId: WorkspaceOauthClient["id"];
676+ scopeOauthClientId: ScopeOauthClient["id"];
667677 sources: ReadonlyArray<{
668678 service: string;
669679 version: string;
@@ -687,8 +697,8 @@ export type ConnectGoogleDiscoveryBatchResult = { · 17 unmodified lines
687697 } | null;
688698 };
689699
690−export type CreateWorkspaceOauthClientInput = {
691− workspaceId: WorkspaceId;
700+export type CreateScopeOauthClientInput = {
701+ scopeId: ScopeId;
692702 providerKey: string;
693703 label?: string | null;
694704 oauthClient: SourceOauthClientInput;
@@ -707,8 +717,8 @@ export type SourceOAuthProviderInput = { · 12 unmodified lines
707717 };
708718
709719 export type StartSourceOAuthSessionInput = {
710− workspaceId: WorkspaceId;
711− actorAccountId?: AccountId | null;
720+ scopeId: ScopeId;
721+ actorScopeId?: ScopeId | null;
712722 provider: SourceOAuthProviderInput;
713723 baseUrl?: string | null;
714724 displayName?: string | null;
@@ -928,11 +938,11 @@ type ResolvedSourceOauthClient = { · 213 unmodified lines
928938 providerKey: string;
929939 clientId: string;
930940 clientSecret: SecretRef | null;
931− redirectMode: WorkspaceSourceOauthClientRedirectMode;
941+ redirectMode: ScopedSourceOauthClientRedirectMode;
932942 };
933943
934−type ResolvedWorkspaceOauthClient = ResolvedSourceOauthClient & {
935− id: WorkspaceOauthClient["id"];
944+type ResolvedScopeOauthClient = ResolvedSourceOauthClient & {
945+ id: ScopeOauthClient["id"];
936946 label: string | null;
937947 };
938948
@@ -954,22 +964,22 @@ type RuntimeProviderAuthResult = · 15 unmodified lines
954964 authorizationUrl: string;
955965 };
956966
957−const decodeWorkspaceSourceOauthClientMetadataOption = Schema.decodeUnknownOption(
958− WorkspaceSourceOauthClientMetadataJsonSchema,
967+const decodeScopedSourceOauthClientMetadataOption = Schema.decodeUnknownOption(
968+ ScopedSourceOauthClientMetadataJsonSchema,
959969 );
960970
961−const encodeWorkspaceSourceOauthClientMetadataJson = Schema.encodeSync(
962− WorkspaceSourceOauthClientMetadataJsonSchema,
971+const encodeScopedSourceOauthClientMetadataJson = Schema.encodeSync(
972+ ScopedSourceOauthClientMetadataJsonSchema,
963973 );
964974
965975 const sourceOauthClientRedirectMode = (client: {
966976 clientMetadataJson: string | null;
967−}): WorkspaceSourceOauthClientRedirectMode => {
977+}): ScopedSourceOauthClientRedirectMode => {
968978 if (client.clientMetadataJson === null) {
969979 return "app_callback";
970980 }
971981
972− const decoded = decodeWorkspaceSourceOauthClientMetadataOption(
982+ const decoded = decodeScopedSourceOauthClientMetadataOption(
973983 client.clientMetadataJson,
974984 );
975985 if (Option.isNone(decoded)) {
@@ -1009,8 +1019,8 @@ const upsertSourceOauthClient = (input: { · 33 unmodified lines
10091019 return yield* runtimeEffectError("sources/source-auth-service", `Source ${input.source.id} does not support OAuth client configuration`);
10101020 }
10111021
1012− const existing = yield* input.executorState.sourceOauthClients.getByWorkspaceSourceAndProvider({
1013− workspaceId: input.source.workspaceId,
1022+ const existing = yield* input.executorState.sourceOauthClients.getByScopeSourceAndProvider({
1023+ scopeId: input.source.scopeId,
10141024 sourceId: input.source.id,
10151025 providerKey: setupConfig.providerKey,
10161026 });
@@ -1029,18 +1039,18 @@ const upsertSourceOauthClient = (input: { · 12 unmodified lines
10291039 const now = Date.now();
10301040 const clientId = Option.isSome(existing)
10311041 ? existing.value.id
1032− : WorkspaceSourceOauthClientIdSchema.make(
1042+ : ScopedSourceOauthClientIdSchema.make(
10331043 `src_oauth_client_${crypto.randomUUID()}`,
10341044 );
10351045 yield* input.executorState.sourceOauthClients.upsert({
10361046 id: clientId,
1037− workspaceId: input.source.workspaceId,
1047+ scopeId: input.source.scopeId,
10381048 sourceId: input.source.id,
10391049 providerKey: setupConfig.providerKey,
10401050 clientId: normalizedOauthClient.clientId,
10411051 clientSecretProviderId: clientSecretRef?.providerId ?? null,
10421052 clientSecretHandle: clientSecretRef?.handle ?? null,
1043− clientMetadataJson: encodeWorkspaceSourceOauthClientMetadataJson({
1053+ clientMetadataJson: encodeScopedSourceOauthClientMetadataJson({
10441054 redirectMode: normalizedOauthClient.redirectMode ?? "app_callback",
10451055 }),
10461056 createdAt: Option.isSome(existing) ? existing.value.createdAt : now,
@@ -1084,8 +1094,8 @@ const resolveExistingSourceOauthClient = (input: { · 37 unmodified lines
10841094 return null;
10851095 }
10861096
1087− const existing = yield* input.executorState.sourceOauthClients.getByWorkspaceSourceAndProvider({
1088− workspaceId: input.source.workspaceId,
1097+ const existing = yield* input.executorState.sourceOauthClients.getByScopeSourceAndProvider({
1098+ scopeId: input.source.scopeId,
10891099 sourceId: input.source.id,
10901100 providerKey: setupConfig.providerKey,
10911101 });
@@ -1101,9 +1111,9 @@ const resolveExistingSourceOauthClient = (input: { · 9 unmodified lines
11011111 };
11021112 });
11031113
1104−const createWorkspaceOauthClient = (input: {
1114+const createScopeOauthClient = (input: {
11051115 executorState: ExecutorStateStoreShape;
1106− workspaceId: WorkspaceId;
1116+ scopeId: ScopeId;
11071117 providerKey: string;
11081118 oauthClient: SourceOauthClientInput;
11091119 label?: string | null;
@@ -1111,7 +1121,7 @@ const createWorkspaceOauthClient = (input: { · 1 unmodified line
11111121 input: SourceOauthClientInput,
11121122 ) => Effect.Effect<SourceOauthClientInput, Error, never>;
11131123 storeSecretMaterial: StoreSecretMaterial;
1114−}): Effect.Effect<ResolvedWorkspaceOauthClient, Error, never> =>
1124+}): Effect.Effect<ResolvedScopeOauthClient, Error, never> =>
11151125 Effect.gen(function* () {
11161126 const normalizedOauthClient = input.normalizeOauthClient
11171127 ? yield* input.normalizeOauthClient(input.oauthClient)
@@ -1123,19 +1133,19 @@ const createWorkspaceOauthClient = (input: { · 5 unmodified lines
11231133 })
11241134 : null;
11251135 const now = Date.now();
1126− const id = WorkspaceOauthClientIdSchema.make(
1136+ const id = ScopeOauthClientIdSchema.make(
11271137 `ws_oauth_client_${crypto.randomUUID()}`,
11281138 );
11291139
1130− yield* input.executorState.workspaceOauthClients.upsert({
1140+ yield* input.executorState.scopeOauthClients.upsert({
11311141 id,
1132− workspaceId: input.workspaceId,
1142+ scopeId: input.scopeId,
11331143 providerKey: input.providerKey,
11341144 label: trimOrNull(input.label) ?? null,
11351145 clientId: normalizedOauthClient.clientId,
11361146 clientSecretProviderId: clientSecretRef?.providerId ?? null,
11371147 clientSecretHandle: clientSecretRef?.handle ?? null,
1138− clientMetadataJson: encodeWorkspaceSourceOauthClientMetadataJson({
1148+ clientMetadataJson: encodeScopedSourceOauthClientMetadataJson({
11391149 redirectMode: normalizedOauthClient.redirectMode ?? "app_callback",
11401150 }),
11411151 createdAt: now,
@@ -1152,23 +1162,28 @@ const createWorkspaceOauthClient = (input: { · 10 unmodified lines
11521162 };
11531163 });
11541164
1155−const resolveWorkspaceOauthClientById = (input: {
1165+const resolveScopeOauthClientById = (input: {
11561166 executorState: ExecutorStateStoreShape;
1157− workspaceId: WorkspaceId;
1158− oauthClientId: WorkspaceOauthClient["id"];
1167+ scopeId: ScopeId;
1168+ oauthClientId: ScopeOauthClient["id"];
11591169 providerKey: string;
1160−}): Effect.Effect<ResolvedWorkspaceOauthClient | null, Error, never> =>
1170+ localScopeState?: RuntimeLocalScopeState;
1171+}): Effect.Effect<ResolvedScopeOauthClient | null, Error, never> =>
11611172 Effect.gen(function* () {
1162− const existing = yield* input.executorState.workspaceOauthClients.getById(input.oauthClientId);
1173+ const existing = yield* input.executorState.scopeOauthClients.getById(input.oauthClientId);
11631174 if (Option.isNone(existing)) {
11641175 return null;
11651176 }
11661177
1178+ const allowedScopeIds = resolvedScopeSearchOrder({
1179+ scopeId: input.scopeId,
1180+ localScopeState: input.localScopeState,
1181+ });
11671182 if (
1168− existing.value.workspaceId !== input.workspaceId
1183+ !allowedScopeIds.includes(existing.value.scopeId)
11691184 || existing.value.providerKey !== input.providerKey
11701185 ) {
1171− return yield* runtimeEffectError("sources/source-auth-service", `Workspace OAuth client ${input.oauthClientId} is not valid for ${input.providerKey}`);
1186+ return yield* runtimeEffectError("sources/source-auth-service", `Scope OAuth client ${input.oauthClientId} is not valid for ${input.providerKey}`);
11721187 }
11731188
11741189 return {
@@ -1192,6 +1207,25 @@ const providerGrantCoversScopes = ( · 17 unmodified lines
11921207 const normalizeScopes = (scopes: ReadonlyArray<string>): ReadonlyArray<string> =>
11931208 [...new Set(scopes.map((scope) => scope.trim()).filter((scope) => scope.length > 0))];
11941209
1210+const resolvedScopeSearchOrder = (input: {
1211+ scopeId: ScopeId;
1212+ localScopeState?: RuntimeLocalScopeState;
1213+}): ReadonlyArray<ScopeId> => {
1214+ const localScopeState = input.localScopeState;
1215+ if (!localScopeState) {
1216+ return [input.scopeId];
1217+ }
1218+
1219+ if (localScopeState.installation.scopeId !== input.scopeId) {
1220+ return [input.scopeId];
1221+ }
1222+
1223+ return [...new Set([
1224+ input.scopeId,
1225+ ...localScopeState.installation.resolutionScopeIds,
1226+ ])];
1227+};
1228+
11951229 const authorizationParamsMatch = (
11961230 left: SourceAdapterOauth2SetupConfig["authorizationParams"],
11971231 right: SourceAdapterOauth2SetupConfig["authorizationParams"],
@@ -1272,34 +1306,44 @@ const mergeRuntimeProviderAuthSetupConfig = ( · 74 unmodified lines
12721306
12731307 const findReusableProviderGrant = (input: {
12741308 executorState: ExecutorStateStoreShape;
1275− workspaceId: WorkspaceId;
1276− actorAccountId?: AccountId | null;
1309+ scopeId: ScopeId;
1310+ actorScopeId?: ScopeId | null;
12771311 providerKey: string;
1278− oauthClientId: WorkspaceOauthClient["id"];
1312+ oauthClientId: ScopeOauthClient["id"];
12791313 requiredScopes: ReadonlyArray<string>;
1314+ localScopeState?: RuntimeLocalScopeState;
12801315 }): Effect.Effect<import("effect/Option").Option<import("#schema").ProviderAuthGrant>, Error, never> =>
1281− Effect.map(
1282− input.executorState.providerAuthGrants.listByWorkspaceActorAndProvider({
1283− workspaceId: input.workspaceId,
1284− actorAccountId: input.actorAccountId ?? null,
1285− providerKey: input.providerKey,
1286− }),
1287− (grants) =>
1288− Option.fromNullable(
1289− grants.find(
1290− (grant) =>
1291− grant.oauthClientId === input.oauthClientId
1292− && providerGrantCoversScopes(grant.grantedScopes, input.requiredScopes),
1293− ),
1294− ),
1295− );
1316+ Effect.gen(function* () {
1317+ const scopeIds = resolvedScopeSearchOrder({
1318+ scopeId: input.scopeId,
1319+ localScopeState: input.localScopeState,
1320+ });
1321+
1322+ for (const candidateScopeId of scopeIds) {
1323+ const grants = yield* input.executorState.providerAuthGrants.listByScopeActorAndProvider({
1324+ scopeId: candidateScopeId,
1325+ actorScopeId: input.actorScopeId ?? null,
1326+ providerKey: input.providerKey,
1327+ });
1328+ const matchingGrant = grants.find(
1329+ (grant) =>
1330+ grant.oauthClientId === input.oauthClientId
1331+ && providerGrantCoversScopes(grant.grantedScopes, input.requiredScopes),
1332+ );
1333+ if (matchingGrant) {
1334+ return Option.some(matchingGrant);
1335+ }
1336+ }
1337+
1338+ return Option.none();
1339+ });
12961340
12971341 const upsertProviderAuthGrant = (input: {
12981342 executorState: ExecutorStateStoreShape;
1299− workspaceId: WorkspaceId;
1300− actorAccountId?: AccountId | null;
1343+ scopeId: ScopeId;
1344+ actorScopeId?: ScopeId | null;
13011345 providerKey: string;
1302− oauthClientId: WorkspaceOauthClient["id"];
1346+ oauthClientId: ScopeOauthClient["id"];
13031347 tokenEndpoint: string;
13041348 clientAuthentication: "none" | "client_secret_post";
13051349 headerName: string;
@@ -1328,8 +1372,8 @@ const upsertProviderAuthGrant = (input: { · 22 unmodified lines
13281372 const now = Date.now();
13291373 const nextGrant = {
13301374 id: existingGrant?.id ?? ProviderAuthGrantIdSchema.make(`provider_grant_${crypto.randomUUID()}`),
1331− workspaceId: input.workspaceId,
1332− actorAccountId: input.actorAccountId ?? null,
1375+ scopeId: input.scopeId,
1376+ actorScopeId: input.actorScopeId ?? null,
13331377 providerKey: input.providerKey,
13341378 oauthClientId: input.oauthClientId,
13351379 tokenEndpoint: input.tokenEndpoint,
@@ -1369,16 +1413,16 @@ const startOauth2PkceSourceCredentialSetup = (input: { · 33 unmodified lines
13691413 executorState: ExecutorStateStoreShape;
13701414 sourceStore: RuntimeSourceStore;
13711415 source: Source;
1372− actorAccountId?: AccountId | null;
1416+ actorScopeId?: ScopeId | null;
13731417 executionId?: SourceAuthSession["executionId"];
13741418 interactionId?: SourceAuthSession["interactionId"];
13751419 baseUrl: string;
1376− redirectModeOverride?: WorkspaceSourceOauthClientRedirectMode;
1420+ redirectModeOverride?: ScopedSourceOauthClientRedirectMode;
13771421 storeSecretMaterial: StoreSecretMaterial;
13781422 }): Effect.Effect<
13791423 Extract<ExecutorSourceAddResult, { kind: "oauth_required" }> | null,
13801424 Error,
1381− WorkspaceStorageServices
1425+ ScopeStorageServices
13821426 > =>
13831427 Effect.gen(function* () {
13841428 const adapter = getSourceAdapterForSource(input.source);
@@ -1404,7 +1448,7 @@ const startOauth2PkceSourceCredentialSetup = (input: { · 19 unmodified lines
14041448 const state = crypto.randomUUID();
14051449 const completionUrl = resolveSourceCredentialOauthCompleteUrl({
14061450 baseUrl: input.baseUrl,
1407− workspaceId: input.source.workspaceId,
1451+ scopeId: input.source.scopeId,
14081452 sourceId: input.source.id,
14091453 });
14101454 const redirectMode = input.redirectModeOverride ?? oauthClient.redirectMode;
@@ -1429,9 +1473,9 @@ const startOauth2PkceSourceCredentialSetup = (input: { · 18 unmodified lines
14291473
14301474 yield* input.executorState.sourceAuthSessions.upsert({
14311475 id: sessionId,
1432− workspaceId: input.source.workspaceId,
1476+ scopeId: input.source.scopeId,
14331477 sourceId: input.source.id,
1434− actorAccountId: input.actorAccountId ?? null,
1478+ actorScopeId: input.actorScopeId ?? null,
14351479 credentialSlot: "runtime",
14361480 executionId: input.executionId ?? null,
14371481 interactionId: input.interactionId ?? null,
@@ -1463,7 +1507,7 @@ const startOauth2PkceSourceCredentialSetup = (input: { · 25 unmodified lines
14631507 });
14641508
14651509 const authRequiredSource = yield* updateSourceStatus(input.sourceStore, input.source, {
1466− actorAccountId: input.actorAccountId,
1510+ actorScopeId: input.actorScopeId,
14671511 status: "auth_required",
14681512 lastError: null,
14691513 });
@@ -1486,13 +1530,13 @@ const startOauth2PkceSourceCredentialSetup = (input: { · 16 unmodified lines
14861530 const startProviderOauthBatchCredentialSetup = (input: {
14871531 executorState: ExecutorStateStoreShape;
14881532 sourceStore: RuntimeSourceStore;
1489− workspaceId: WorkspaceId;
1490− actorAccountId?: AccountId | null;
1533+ scopeId: ScopeId;
1534+ actorScopeId?: ScopeId | null;
14911535 executionId?: SourceAuthSession["executionId"];
14921536 interactionId?: SourceAuthSession["interactionId"];
14931537 baseUrl: string;
1494− redirectModeOverride?: WorkspaceSourceOauthClientRedirectMode;
1495− workspaceOauthClient: ResolvedWorkspaceOauthClient;
1538+ redirectModeOverride?: ScopedSourceOauthClientRedirectMode;
1539+ scopeOauthClient: ResolvedScopeOauthClient;
14961540 setupConfig: SourceAdapterOauth2SetupConfig;
14971541 targetSources: ReadonlyArray<{
14981542 source: Source;
@@ -1502,7 +1546,7 @@ const startProviderOauthBatchCredentialSetup = (input: { · 3 unmodified lines
15021546 sessionId: SourceAuthSession["id"];
15031547 authorizationUrl: string;
15041548 source: Source;
1505−}, Error, WorkspaceStorageServices> =>
1549+}, Error, ScopeStorageServices> =>
15061550 Effect.gen(function* () {
15071551 if (input.targetSources.length === 0) {
15081552 return yield* runtimeEffectError("sources/source-auth-service", "Provider OAuth setup requires at least one target source");
@@ -1512,9 +1556,9 @@ const startProviderOauthBatchCredentialSetup = (input: { · 3 unmodified lines
15121556 const state = crypto.randomUUID();
15131557 const completionUrl = resolveWorkspaceProviderOauthCompleteUrl({
15141558 baseUrl: input.baseUrl,
1515− workspaceId: input.workspaceId,
1559+ scopeId: input.scopeId,
15161560 });
1517− const redirectMode = input.redirectModeOverride ?? input.workspaceOauthClient.redirectMode;
1561+ const redirectMode = input.redirectModeOverride ?? input.scopeOauthClient.redirectMode;
15181562 const redirectServer = redirectMode === "loopback"
15191563 ? yield* startOauthLoopbackRedirectServer({
15201564 completionUrl,
@@ -1526,7 +1570,7 @@ const startProviderOauthBatchCredentialSetup = (input: { · 5 unmodified lines
15261570 return yield* Effect.gen(function* () {
15271571 const authorizationUrl = buildOAuth2AuthorizationUrl({
15281572 authorizationEndpoint: input.setupConfig.authorizationEndpoint,
1529− clientId: input.workspaceOauthClient.clientId,
1573+ clientId: input.scopeOauthClient.clientId,
15301574 redirectUri,
15311575 scopes: [...normalizeScopes(input.setupConfig.scopes)],
15321576 state,
@@ -1537,9 +1581,9 @@ const startProviderOauthBatchCredentialSetup = (input: { · 4 unmodified lines
15371581
15381582 yield* input.executorState.sourceAuthSessions.upsert({
15391583 id: sessionId,
1540− workspaceId: input.workspaceId,
1584+ scopeId: input.scopeId,
15411585 sourceId: SourceIdSchema.make(`oauth_provider_${crypto.randomUUID()}`),
1542− actorAccountId: input.actorAccountId ?? null,
1586+ actorScopeId: input.actorScopeId ?? null,
15431587 credentialSlot: "runtime",
15441588 executionId: input.executionId ?? null,
15451589 interactionId: input.interactionId ?? null,
@@ -1552,7 +1596,7 @@ const startProviderOauthBatchCredentialSetup = (input: { · 6 unmodified lines
15521596 authorizationEndpoint: input.setupConfig.authorizationEndpoint,
15531597 tokenEndpoint: input.setupConfig.tokenEndpoint,
15541598 redirectUri,
1555− oauthClientId: input.workspaceOauthClient.id,
1599+ oauthClientId: input.scopeOauthClient.id,
15561600 clientAuthentication: input.setupConfig.clientAuthentication,
15571601 scopes: [...normalizeScopes(input.setupConfig.scopes)],
15581602 headerName: input.setupConfig.headerName,
@@ -1577,7 +1621,7 @@ const startProviderOauthBatchCredentialSetup = (input: { · 18 unmodified lines
15771621 input.sourceStore,
15781622 input.targetSources[0]!.source,
15791623 {
1580− actorAccountId: input.actorAccountId,
1624+ actorScopeId: input.actorScopeId,
15811625 status: "auth_required",
15821626 lastError: null,
15831627 },
@@ -1587,7 +1631,7 @@ const startProviderOauthBatchCredentialSetup = (input: { · 3 unmodified lines
15871631 input.targetSources.slice(1),
15881632 (target) =>
15891633 updateSourceStatus(input.sourceStore, target.source, {
1590− actorAccountId: input.actorAccountId,
1634+ actorScopeId: input.actorScopeId,
15911635 status: "auth_required",
15921636 lastError: null,
15931637 }).pipe(Effect.asVoid),
@@ -1612,31 +1656,33 @@ const connectSourcesWithProviderRuntimeAuth = (input: { · 18 unmodified lines
16121656 executorState: ExecutorStateStoreShape;
16131657 sourceStore: RuntimeSourceStore;
16141658 sourceCatalogSync: RuntimeSourceCatalogSyncShape;
1615− workspaceId: WorkspaceId;
1616− actorAccountId?: AccountId | null;
1659+ scopeId: ScopeId;
1660+ actorScopeId?: ScopeId | null;
16171661 executionId?: SourceAuthSession["executionId"];
16181662 interactionId?: SourceAuthSession["interactionId"];
16191663 baseUrl?: string | null;
16201664 getLocalServerBaseUrl?: () => string | undefined;
1621− workspaceOauthClient: ResolvedWorkspaceOauthClient;
1665+ scopeOauthClient: ResolvedScopeOauthClient;
16221666 targets: ReadonlyArray<RuntimeProviderAuthTarget>;
1623−}): Effect.Effect<RuntimeProviderAuthResult, Error, WorkspaceStorageServices> =>
1667+ localScopeState?: RuntimeLocalScopeState;
1668+}): Effect.Effect<RuntimeProviderAuthResult, Error, ScopeStorageServices> =>
16241669 Effect.gen(function* () {
16251670 const setupConfig = yield* mergeRuntimeProviderAuthSetupConfig(input.targets);
16261671 const reusableGrant = yield* findReusableProviderGrant({
16271672 executorState: input.executorState,
1628− workspaceId: input.workspaceId,
1629− actorAccountId: input.actorAccountId,
1673+ scopeId: input.scopeId,
1674+ actorScopeId: input.actorScopeId,
16301675 providerKey: setupConfig.providerKey,
1631− oauthClientId: input.workspaceOauthClient.id,
1676+ oauthClientId: input.scopeOauthClient.id,
16321677 requiredScopes: setupConfig.scopes,
1678+ localScopeState: input.localScopeState,
16331679 });
16341680
16351681 if (Option.isSome(reusableGrant)) {
16361682 const connectedSources = yield* attachProviderGrantToSources({
16371683 sourceStore: input.sourceStore,
16381684 sourceCatalogSync: input.sourceCatalogSync,
1639− actorAccountId: input.actorAccountId,
1685+ actorScopeId: input.actorScopeId,
16401686 grantId: reusableGrant.value.id,
16411687 providerKey: reusableGrant.value.providerKey,
16421688 headerName: reusableGrant.value.headerName,
@@ -1665,13 +1711,13 @@ const connectSourcesWithProviderRuntimeAuth = (input: { · 22 unmodified lines
16651711 const oauthRequired = yield* startProviderOauthBatchCredentialSetup({
16661712 executorState: input.executorState,
16671713 sourceStore: input.sourceStore,
1668− workspaceId: input.workspaceId,
1669− actorAccountId: input.actorAccountId,
1714+ scopeId: input.scopeId,
1715+ actorScopeId: input.actorScopeId,
16701716 executionId: input.executionId,
16711717 interactionId: input.interactionId,
16721718 baseUrl,
16731719 redirectModeOverride: requestBaseUrl ? "app_callback" : undefined,
1674− workspaceOauthClient: input.workspaceOauthClient,
1720+ scopeOauthClient: input.scopeOauthClient,
16751721 setupConfig,
16761722 targetSources: input.targets.map((target) => ({
16771723 source: target.source,
@@ -1683,9 +1729,9 @@ const connectSourcesWithProviderRuntimeAuth = (input: { · 5 unmodified lines
16831729 input.targets,
16841730 (target) =>
16851731 input.sourceStore.loadSourceById({
1686− workspaceId: target.source.workspaceId,
1732+ scopeId: target.source.scopeId,
16871733 sourceId: target.source.id,
1688− actorAccountId: input.actorAccountId,
1734+ actorScopeId: input.actorScopeId,
16891735 }),
16901736 { discard: false },
16911737 );
@@ -1701,7 +1747,7 @@ const connectSourcesWithProviderRuntimeAuth = (input: { · 9 unmodified lines
17011747 const attachProviderGrantToSources = (input: {
17021748 sourceStore: RuntimeSourceStore;
17031749 sourceCatalogSync: RuntimeSourceCatalogSyncShape;
1704− actorAccountId?: AccountId | null;
1750+ actorScopeId?: ScopeId | null;
17051751 grantId: ProviderAuthGrant["id"];
17061752 providerKey: string;
17071753 headerName: string;
@@ -1710,13 +1756,13 @@ const attachProviderGrantToSources = (input: { · 2 unmodified lines
17101756 source: Source;
17111757 requiredScopes: ReadonlyArray<string>;
17121758 }>;
1713−}): Effect.Effect<ReadonlyArray<Source>, Error, WorkspaceStorageServices> =>
1759+}): Effect.Effect<ReadonlyArray<Source>, Error, ScopeStorageServices> =>
17141760 Effect.forEach(
17151761 input.targets,
17161762 (target) =>
17171763 Effect.gen(function* () {
17181764 const connectedSource = yield* updateSourceStatus(input.sourceStore, target.source, {
1719− actorAccountId: input.actorAccountId,
1765+ actorScopeId: input.actorScopeId,
17201766 status: "connected",
17211767 lastError: null,
17221768 auth: {
@@ -1731,7 +1777,7 @@ const attachProviderGrantToSources = (input: { · 8 unmodified lines
17311777
17321778 yield* input.sourceCatalogSync.sync({
17331779 source: connectedSource,
1734− actorAccountId: input.actorAccountId,
1780+ actorScopeId: input.actorScopeId,
17351781 });
17361782
17371783 return connectedSource;
@@ -1742,18 +1788,18 @@ const attachProviderGrantToSources = (input: { · 4 unmodified lines
17421788 const removeProviderAuthGrantInternal = (input: {
17431789 executorState: ExecutorStateStoreShape;
17441790 sourceStore: RuntimeSourceStore;
1745− workspaceId: WorkspaceId;
1791+ scopeId: ScopeId;
17461792 grantId: ProviderAuthGrant["id"];
17471793 deleteSecretMaterial: DeleteSecretMaterial;
1748−}): Effect.Effect<boolean, Error, WorkspaceStorageServices> =>
1794+}): Effect.Effect<boolean, Error, ScopeStorageServices> =>
17491795 Effect.gen(function* () {
17501796 const grantOption = yield* input.executorState.providerAuthGrants.getById(input.grantId);
1751− if (Option.isNone(grantOption) || grantOption.value.workspaceId !== input.workspaceId) {
1797+ if (Option.isNone(grantOption) || grantOption.value.scopeId !== input.scopeId) {
17521798 return false;
17531799 }
17541800
17551801 const references = yield* listProviderGrantRefArtifacts(input.executorState, {
1756− workspaceId: input.workspaceId,
1802+ scopeId: input.scopeId,
17571803 grantId: input.grantId,
17581804 });
17591805
@@ -1762,9 +1808,9 @@ const removeProviderAuthGrantInternal = (input: { · 2 unmodified lines
17621808 (artifact) =>
17631809 Effect.gen(function* () {
17641810 const latestSource = yield* input.sourceStore.loadSourceById({
1765− workspaceId: artifact.workspaceId,
1811+ scopeId: artifact.scopeId,
17661812 sourceId: artifact.sourceId,
1767− actorAccountId: artifact.actorAccountId,
1813+ actorScopeId: artifact.actorScopeId,
17681814 }).pipe(
17691815 Effect.catchAll(() => Effect.succeed(null)),
17701816 );
@@ -1773,10 +1819,10 @@ const removeProviderAuthGrantInternal = (input: { · 2 unmodified lines
17731819 yield* removeAuthLeaseAndSecrets(input.executorState, {
17741820 authArtifactId: artifact.id,
17751821 }, input.deleteSecretMaterial);
1776− yield* input.executorState.authArtifacts.removeByWorkspaceSourceAndActor({
1777− workspaceId: artifact.workspaceId,
1822+ yield* input.executorState.authArtifacts.removeByScopeSourceAndActor({
1823+ scopeId: artifact.scopeId,
17781824 sourceId: artifact.sourceId,
1779− actorAccountId: artifact.actorAccountId,
1825+ actorScopeId: artifact.actorScopeId,
17801826 slot: artifact.slot,
17811827 });
17821828 return;
@@ -1790,7 +1836,7 @@ const removeProviderAuthGrantInternal = (input: { · 7 unmodified lines
17901836 importAuth: artifact.slot === "import" ? { kind: "none" } : latestSource.importAuth,
17911837 updatedAt: Date.now(),
17921838 }, {
1793− actorAccountId: artifact.actorAccountId,
1839+ actorScopeId: artifact.actorScopeId,
17941840 }).pipe(Effect.asVoid);
17951841 }),
17961842 { discard: true },
@@ -1809,8 +1855,8 @@ const connectMcpSourceInternal = (input: { · 12 unmodified lines
18091855 sourceCatalogSync: RuntimeSourceCatalogSyncShape;
18101856 getLocalServerBaseUrl?: () => string | undefined;
18111857 baseUrl?: string | null;
1812− workspaceId: WorkspaceId;
1813− actorAccountId?: AccountId | null;
1858+ scopeId: ScopeId;
1859+ actorScopeId?: ScopeId | null;
18141860 sourceId?: Source["id"] | null;
18151861 executionId?: SourceAuthSession["executionId"];
18161862 interactionId?: SourceAuthSession["interactionId"];
@@ -1827,7 +1873,7 @@ const connectMcpSourceInternal = (input: { · 10 unmodified lines
18271873 cwd?: string | null;
18281874 mcpDiscoveryElicitation?: McpDiscoveryElicitationContext;
18291875 resolveSecretMaterial: ResolveSecretMaterial;
1830−}): Effect.Effect<McpSourceConnectResult, Error, WorkspaceStorageServices> =>
1876+}): Effect.Effect<McpSourceConnectResult, Error, ScopeStorageServices> =>
18311877 Effect.gen(function* () {
18321878 const lookupEndpoint = input.sourceId
18331879 ? null
@@ -1840,9 +1886,9 @@ const connectMcpSourceInternal = (input: { · 6 unmodified lines
18401886 const existing = yield* (
18411887 input.sourceId
18421888 ? input.sourceStore.loadSourceById({
1843− workspaceId: input.workspaceId,
1889+ scopeId: input.scopeId,
18441890 sourceId: input.sourceId,
1845− actorAccountId: input.actorAccountId,
1891+ actorScopeId: input.actorScopeId,
18461892 }).pipe(
18471893 Effect.flatMap((source) =>
18481894 sourceAdapterRequiresInteractiveConnect(source.kind)
@@ -1850,8 +1896,8 @@ const connectMcpSourceInternal = (input: { · 1 unmodified line
18501896 : Effect.fail(runtimeEffectError("sources/source-auth-service", `Expected MCP source, received ${source.kind}`)),
18511897 ),
18521898 )
1853− : input.sourceStore.loadSourcesInWorkspace(input.workspaceId, {
1854− actorAccountId: input.actorAccountId,
1899+ : input.sourceStore.loadSourcesInScope(input.scopeId, {
1900+ actorScopeId: input.actorScopeId,
18551901 }).pipe(
18561902 Effect.map((sources) =>
18571903 sources.find(
@@ -1943,7 +1989,7 @@ const connectMcpSourceInternal = (input: { · 85 unmodified lines
19431989 now,
19441990 })
19451991 : yield* createSourceFromPayload({
1946− workspaceId: input.workspaceId,
1992+ scopeId: input.scopeId,
19471993 sourceId: SourceIdSchema.make(`src_${crypto.randomUUID()}`),
19481994 payload: {
19491995 name: chosenName,
@@ -1969,11 +2015,11 @@ const connectMcpSourceInternal = (input: { · 19 unmodified lines
19692015 });
19702016
19712017 const persistedDraft = yield* input.sourceStore.persistSource(draftSource, {
1972− actorAccountId: input.actorAccountId,
2018+ actorScopeId: input.actorScopeId,
19732019 });
19742020 yield* input.sourceCatalogSync.sync({
19752021 source: persistedDraft,
1976− actorAccountId: input.actorAccountId,
2022+ actorScopeId: input.actorScopeId,
19772023 });
19782024
19792025 const discovered = yield* Effect.either(
@@ -1988,7 +2034,7 @@ const connectMcpSourceInternal = (input: { · 8 unmodified lines
19882034 onRight: (result) =>
19892035 Effect.gen(function* () {
19902036 const connected = yield* updateSourceStatus(input.sourceStore, persistedDraft, {
1991− actorAccountId: input.actorAccountId,
2037+ actorScopeId: input.actorScopeId,
19922038 status: "connected",
19932039 lastError: null,
19942040 auth: { kind: "none" },
@@ -2003,7 +2049,7 @@ const connectMcpSourceInternal = (input: { · 8 unmodified lines
20032049 return yield* Either.match(indexed, {
20042050 onLeft: (error) =>
20052051 updateSourceStatus(input.sourceStore, connected, {
2006− actorAccountId: input.actorAccountId,
2052+ actorScopeId: input.actorScopeId,
20072053 status: "error",
20082054 lastError: error.message,
20092055 }).pipe(
@@ -2031,7 +2077,7 @@ const connectMcpSourceInternal = (input: { · 21 unmodified lines
20312077 const state = crypto.randomUUID();
20322078 const redirectUrl = resolveSourceCredentialOauthCompleteUrl({
20332079 baseUrl: localServerBaseUrl,
2034− workspaceId: input.workspaceId,
2080+ scopeId: input.scopeId,
20352081 sourceId: persistedDraft.id,
20362082 });
20372083 const oauthStart = yield* startMcpOAuthAuthorization({
@@ -2041,7 +2087,7 @@ const connectMcpSourceInternal = (input: { · 3 unmodified lines
20412087 });
20422088
20432089 const authRequiredSource = yield* updateSourceStatus(input.sourceStore, persistedDraft, {
2044− actorAccountId: input.actorAccountId,
2090+ actorScopeId: input.actorScopeId,
20452091 status: "auth_required",
20462092 lastError: null,
20472093 });
@@ -2049,9 +2095,9 @@ const connectMcpSourceInternal = (input: { · 1 unmodified line
20492095 const sessionNow = Date.now();
20502096 yield* input.executorState.sourceAuthSessions.upsert({
20512097 id: sessionId,
2052− workspaceId: input.workspaceId,
2098+ scopeId: input.scopeId,
20532099 sourceId: authRequiredSource.id,
2054− actorAccountId: input.actorAccountId ?? null,
2100+ actorScopeId: input.actorScopeId ?? null,
20552101 credentialSlot: "runtime",
20562102 executionId: input.executionId ?? null,
20572103 interactionId: input.interactionId ?? null,
@@ -2095,16 +2141,17 @@ const addExecutorHttpSource = (input: { · 37 unmodified lines
20952141 resolveSecretMaterial: ResolveSecretMaterial;
20962142 getLocalServerBaseUrl?: () => string | undefined;
20972143 baseUrl?: string | null;
2098−}): Effect.Effect<ExecutorSourceAddResult, Error, WorkspaceStorageServices> =>
2144+ localScopeState?: RuntimeLocalScopeState;
2145+}): Effect.Effect<ExecutorSourceAddResult, Error, ScopeStorageServices> =>
20992146 Effect.gen(function* () {
21002147 const normalizedEndpoint = normalizeEndpoint(input.sourceInput.endpoint);
21012148 const normalizedSpecUrl = input.sourceInput.kind === "openapi"
21022149 ? normalizeEndpoint(input.sourceInput.specUrl)
21032150 : null;
2104− const existingSources = yield* input.sourceStore.loadSourcesInWorkspace(
2105− input.sourceInput.workspaceId,
2151+ const existingSources = yield* input.sourceStore.loadSourcesInScope(
2152+ input.sourceInput.scopeId,
21062153 {
2107− actorAccountId: input.sourceInput.actorAccountId,
2154+ actorScopeId: input.sourceInput.actorScopeId,
21082155 },
21092156 );
21102157 const existing = existingSources.find((source) => {
@@ -2175,7 +2222,7 @@ const addExecutorHttpSource = (input: { · 64 unmodified lines
21752222 now,
21762223 })
21772224 : yield* createSourceFromPayload({
2178− workspaceId: input.sourceInput.workspaceId,
2225+ scopeId: input.sourceInput.scopeId,
21792226 sourceId: SourceIdSchema.make(`src_${crypto.randomUUID()}`),
21802227 payload: {
21812228 name: chosenName,
@@ -2200,7 +2247,7 @@ const addExecutorHttpSource = (input: { · 18 unmodified lines
22002247 });
22012248
22022249 const persistedDraft = yield* input.sourceStore.persistSource(draftSource, {
2203− actorAccountId: input.sourceInput.actorAccountId,
2250+ actorScopeId: input.sourceInput.actorScopeId,
22042251 });
22052252
22062253 if (shouldPromptForExecutorHttpRuntimeCredentialSetup({
@@ -2215,7 +2262,7 @@ const addExecutorHttpSource = (input: { · 8 unmodified lines
22152262 executorState: input.executorState,
22162263 sourceStore: input.sourceStore,
22172264 source: persistedDraft,
2218− actorAccountId: input.sourceInput.actorAccountId,
2265+ actorScopeId: input.sourceInput.actorScopeId,
22192266 executionId: input.sourceInput.executionId,
22202267 interactionId: input.sourceInput.interactionId,
22212268 baseUrl,
@@ -2228,7 +2275,7 @@ const addExecutorHttpSource = (input: { · 6 unmodified lines
22282275 }
22292276
22302277 const authRequiredSource = yield* updateSourceStatus(input.sourceStore, persistedDraft, {
2231− actorAccountId: input.sourceInput.actorAccountId,
2278+ actorScopeId: input.sourceInput.actorScopeId,
22322279 status: "auth_required",
22332280 lastError: null,
22342281 });
@@ -2246,7 +2293,7 @@ const addExecutorHttpSource = (input: { · 11 unmodified lines
22462293 ...persistedDraft,
22472294 status: "connected",
22482295 },
2249− actorAccountId: input.sourceInput.actorAccountId,
2296+ actorScopeId: input.sourceInput.actorScopeId,
22502297 }),
22512298 );
22522299
@@ -2254,7 +2301,7 @@ const addExecutorHttpSource = (input: { · 1 unmodified line
22542301 onLeft: (error) =>
22552302 isSourceCredentialRequiredError(error)
22562303 ? updateSourceStatus(input.sourceStore, persistedDraft, {
2257− actorAccountId: input.sourceInput.actorAccountId,
2304+ actorScopeId: input.sourceInput.actorScopeId,
22582305 status: "auth_required",
22592306 lastError: null,
22602307 }).pipe(
@@ -2267,7 +2314,7 @@ const addExecutorHttpSource = (input: { · 6 unmodified lines
22672314 ),
22682315 )
22692316 : updateSourceStatus(input.sourceStore, persistedDraft, {
2270− actorAccountId: input.sourceInput.actorAccountId,
2317+ actorScopeId: input.sourceInput.actorScopeId,
22712318 status: "error",
22722319 lastError: error.message,
22732320 }).pipe(
@@ -2275,7 +2322,7 @@ const addExecutorHttpSource = (input: { · 1 unmodified line
22752322 ),
22762323 onRight: () =>
22772324 updateSourceStatus(input.sourceStore, persistedDraft, {
2278− actorAccountId: input.sourceInput.actorAccountId,
2325+ actorScopeId: input.sourceInput.actorScopeId,
22792326 status: "connected",
22802327 lastError: null,
22812328 }).pipe(
@@ -2308,7 +2355,8 @@ const addExecutorGoogleDiscoverySource = (input: { · 26 unmodified lines
23082355 resolveSecretMaterial: ResolveSecretMaterial;
23092356 getLocalServerBaseUrl?: () => string | undefined;
23102357 baseUrl?: string | null;
2311−}): Effect.Effect<ExecutorSourceAddResult, Error, WorkspaceStorageServices> =>
2358+ localScopeState?: RuntimeLocalScopeState;
2359+}): Effect.Effect<ExecutorSourceAddResult, Error, ScopeStorageServices> =>
23122360 Effect.gen(function* () {
23132361 const normalizedService = input.sourceInput.service.trim();
23142362 const normalizedVersion = input.sourceInput.version.trim();
@@ -2316,10 +2364,10 @@ const addExecutorGoogleDiscoverySource = (input: { · 1 unmodified line
23162364 trimOrNull(input.sourceInput.discoveryUrl)
23172365 ?? defaultGoogleDiscoveryUrl(normalizedService, normalizedVersion),
23182366 );
2319− const existingSources = yield* input.sourceStore.loadSourcesInWorkspace(
2320− input.sourceInput.workspaceId,
2367+ const existingSources = yield* input.sourceStore.loadSourcesInScope(
2368+ input.sourceInput.scopeId,
23212369 {
2322− actorAccountId: input.sourceInput.actorAccountId,
2370+ actorScopeId: input.sourceInput.actorScopeId,
23232371 },
23242372 );
23252373 const existing = existingSources.find((source) => {
@@ -2397,7 +2445,7 @@ const addExecutorGoogleDiscoverySource = (input: { · 71 unmodified lines
23972445 now,
23982446 })
23992447 : yield* createSourceFromPayload({
2400− workspaceId: input.sourceInput.workspaceId,
2448+ scopeId: input.sourceInput.scopeId,
24012449 sourceId: SourceIdSchema.make(`src_${crypto.randomUUID()}`),
24022450 payload: {
24032451 name: chosenName,
@@ -2421,7 +2469,7 @@ const addExecutorGoogleDiscoverySource = (input: { · 17 unmodified lines
24212469 });
24222470
24232471 const persistedDraft = yield* input.sourceStore.persistSource(draftSource, {
2424− actorAccountId: input.sourceInput.actorAccountId,
2472+ actorScopeId: input.sourceInput.actorScopeId,
24252473 });
24262474 const googleAdapter = getSourceAdapterForSource(persistedDraft);
24272475 const providerAuthTarget = yield* resolveRuntimeProviderAuthTarget(persistedDraft);
@@ -2431,18 +2479,19 @@ const addExecutorGoogleDiscoverySource = (input: { · 3 unmodified lines
24312479 && input.sourceInput.auth === undefined
24322480 && persistedDraft.auth.kind === "none"
24332481 ) {
2434− let workspaceOauthClient: ResolvedWorkspaceOauthClient | null = null;
2435− if (input.sourceInput.workspaceOauthClientId) {
2436− workspaceOauthClient = yield* resolveWorkspaceOauthClientById({
2482+ let scopeOauthClient: ResolvedScopeOauthClient | null = null;
2483+ if (input.sourceInput.scopeOauthClientId) {
2484+ scopeOauthClient = yield* resolveScopeOauthClientById({
24372485 executorState: input.executorState,
2438− workspaceId: persistedDraft.workspaceId,
2439− oauthClientId: input.sourceInput.workspaceOauthClientId,
2486+ scopeId: persistedDraft.scopeId,
2487+ oauthClientId: input.sourceInput.scopeOauthClientId,
24402488 providerKey: providerAuthTarget.setupConfig.providerKey,
2489+ localScopeState: input.localScopeState,
24412490 });
24422491 } else if (input.sourceInput.oauthClient) {
2443− workspaceOauthClient = yield* createWorkspaceOauthClient({
2492+ scopeOauthClient = yield* createScopeOauthClient({
24442493 executorState: input.executorState,
2445− workspaceId: persistedDraft.workspaceId,
2494+ scopeId: persistedDraft.scopeId,
24462495 providerKey: providerAuthTarget.setupConfig.providerKey,
24472496 oauthClient: input.sourceInput.oauthClient,
24482497 label: `${chosenName} OAuth Client`,
@@ -2451,7 +2500,7 @@ const addExecutorGoogleDiscoverySource = (input: { · 2 unmodified lines
24512500 });
24522501 }
24532502
2454− if (workspaceOauthClient === null) {
2503+ if (scopeOauthClient === null) {
24552504 return yield* runtimeEffectError(
24562505 "sources/source-auth-service",
24572506 `${providerAuthTarget.setupConfig.providerKey} shared auth requires a workspace OAuth client`,
@@ -2462,14 +2511,15 @@ const addExecutorGoogleDiscoverySource = (input: { · 4 unmodified lines
24622511 executorState: input.executorState,
24632512 sourceStore: input.sourceStore,
24642513 sourceCatalogSync: input.sourceCatalogSync,
2465− workspaceId: persistedDraft.workspaceId,
2466− actorAccountId: input.sourceInput.actorAccountId,
2514+ scopeId: persistedDraft.scopeId,
2515+ actorScopeId: input.sourceInput.actorScopeId,
24672516 executionId: input.sourceInput.executionId,
24682517 interactionId: input.sourceInput.interactionId,
24692518 baseUrl: input.baseUrl,
24702519 getLocalServerBaseUrl: input.getLocalServerBaseUrl,
2471− workspaceOauthClient,
2520+ scopeOauthClient,
24722521 targets: [providerAuthTarget],
2522+ localScopeState: input.localScopeState,
24732523 });
24742524
24752525 return providerAuthResult.kind === "connected"
@@ -2507,7 +2557,7 @@ const addExecutorGoogleDiscoverySource = (input: { · 31 unmodified lines
25072557 executorState: input.executorState,
25082558 sourceStore: input.sourceStore,
25092559 source: persistedDraft,
2510− actorAccountId: input.sourceInput.actorAccountId,
2560+ actorScopeId: input.sourceInput.actorScopeId,
25112561 executionId: input.sourceInput.executionId,
25122562 interactionId: input.sourceInput.interactionId,
25132563 baseUrl,
@@ -2520,7 +2570,7 @@ const addExecutorGoogleDiscoverySource = (input: { · 6 unmodified lines
25202570 }
25212571
25222572 const authRequiredSource = yield* updateSourceStatus(input.sourceStore, persistedDraft, {
2523− actorAccountId: input.sourceInput.actorAccountId,
2573+ actorScopeId: input.sourceInput.actorScopeId,
25242574 status: "auth_required",
25252575 lastError: null,
25262576 });
@@ -2538,7 +2588,7 @@ const addExecutorGoogleDiscoverySource = (input: { · 11 unmodified lines
25382588 ...persistedDraft,
25392589 status: "connected",
25402590 },
2541− actorAccountId: input.sourceInput.actorAccountId,
2591+ actorScopeId: input.sourceInput.actorScopeId,
25422592 }),
25432593 );
25442594
@@ -2546,7 +2596,7 @@ const addExecutorGoogleDiscoverySource = (input: { · 1 unmodified line
25462596 onLeft: (error) =>
25472597 isSourceCredentialRequiredError(error)
25482598 ? updateSourceStatus(input.sourceStore, persistedDraft, {
2549− actorAccountId: input.sourceInput.actorAccountId,
2599+ actorScopeId: input.sourceInput.actorScopeId,
25502600 status: "auth_required",
25512601 lastError: null,
25522602 }).pipe(
@@ -2559,7 +2609,7 @@ const addExecutorGoogleDiscoverySource = (input: { · 6 unmodified lines
25592609 ),
25602610 )
25612611 : updateSourceStatus(input.sourceStore, persistedDraft, {
2562− actorAccountId: input.sourceInput.actorAccountId,
2612+ actorScopeId: input.sourceInput.actorScopeId,
25632613 status: "error",
25642614 lastError: error.message,
25652615 }).pipe(
@@ -2567,7 +2617,7 @@ const addExecutorGoogleDiscoverySource = (input: { · 1 unmodified line
25672617 ),
25682618 onRight: () =>
25692619 updateSourceStatus(input.sourceStore, persistedDraft, {
2570− actorAccountId: input.sourceInput.actorAccountId,
2620+ actorScopeId: input.sourceInput.actorScopeId,
25712621 status: "connected",
25722622 lastError: null,
25732623 }).pipe(
@@ -2587,16 +2637,17 @@ const connectGoogleDiscoveryBatchInternal = (input: { · 13 unmodified lines
25872637 sourceCatalogSync: RuntimeSourceCatalogSyncShape;
25882638 sourceInput: ConnectGoogleDiscoveryBatchInput;
25892639 getLocalServerBaseUrl?: () => string | undefined;
2590−}): Effect.Effect<ConnectGoogleDiscoveryBatchResult, Error, WorkspaceStorageServices> =>
2640+ localScopeState?: RuntimeLocalScopeState;
2641+}): Effect.Effect<ConnectGoogleDiscoveryBatchResult, Error, ScopeStorageServices> =>
25912642 Effect.gen(function* () {
25922643 if (input.sourceInput.sources.length === 0) {
25932644 return yield* runtimeEffectError("sources/source-auth-service", "Google batch connect requires at least one source");
25942645 }
25952646
2596− const existingSources = yield* input.sourceStore.loadSourcesInWorkspace(
2597− input.sourceInput.workspaceId,
2647+ const existingSources = yield* input.sourceStore.loadSourcesInScope(
2648+ input.sourceInput.scopeId,
25982649 {
2599− actorAccountId: input.sourceInput.actorAccountId,
2650+ actorScopeId: input.sourceInput.actorScopeId,
26002651 },
26012652 );
26022653
@@ -2664,7 +2715,7 @@ const connectGoogleDiscoveryBatchInternal = (input: { · 61 unmodified lines
26642715 now,
26652716 })
26662717 : yield* createSourceFromPayload({
2667− workspaceId: input.sourceInput.workspaceId,
2718+ scopeId: input.sourceInput.scopeId,
26682719 sourceId: SourceIdSchema.make(`src_${crypto.randomUUID()}`),
26692720 payload: {
26702721 name: chosenName,
@@ -2688,7 +2739,7 @@ const connectGoogleDiscoveryBatchInternal = (input: { · 17 unmodified lines
26882739 });
26892740
26902741 const persistedDraft = yield* input.sourceStore.persistSource(draftSource, {
2691− actorAccountId: input.sourceInput.actorAccountId,
2742+ actorScopeId: input.sourceInput.actorScopeId,
26922743 });
26932744 const providerAuthTarget = yield* resolveRuntimeProviderAuthTarget(persistedDraft);
26942745 if (providerAuthTarget === null) {
@@ -2700,28 +2751,30 @@ const connectGoogleDiscoveryBatchInternal = (input: { · 5 unmodified lines
27002751 { discard: false },
27012752 );
27022753
2703− const workspaceOauthClient = yield* resolveWorkspaceOauthClientById({
2754+ const scopeOauthClient = yield* resolveScopeOauthClientById({
27042755 executorState: input.executorState,
2705− workspaceId: input.sourceInput.workspaceId,
2706− oauthClientId: input.sourceInput.workspaceOauthClientId,
2756+ scopeId: input.sourceInput.scopeId,
2757+ oauthClientId: input.sourceInput.scopeOauthClientId,
27072758 providerKey: persistedTargets[0]!.setupConfig.providerKey,
2759+ localScopeState: input.localScopeState,
27082760 });
2709− if (workspaceOauthClient === null) {
2710− return yield* runtimeEffectError("sources/source-auth-service", `Workspace OAuth client not found: ${input.sourceInput.workspaceOauthClientId}`);
2761+ if (scopeOauthClient === null) {
2762+ return yield* runtimeEffectError("sources/source-auth-service", `Scope OAuth client not found: ${input.sourceInput.scopeOauthClientId}`);
27112763 }
27122764
27132765 const providerAuthResult = yield* connectSourcesWithProviderRuntimeAuth({
27142766 executorState: input.executorState,
27152767 sourceStore: input.sourceStore,
27162768 sourceCatalogSync: input.sourceCatalogSync,
2717− workspaceId: input.sourceInput.workspaceId,
2718− actorAccountId: input.sourceInput.actorAccountId,
2769+ scopeId: input.sourceInput.scopeId,
2770+ actorScopeId: input.sourceInput.actorScopeId,
27192771 executionId: input.sourceInput.executionId,
27202772 interactionId: input.sourceInput.interactionId,
27212773 baseUrl: input.sourceInput.baseUrl,
27222774 getLocalServerBaseUrl: input.getLocalServerBaseUrl,
2723− workspaceOauthClient,
2775+ scopeOauthClient,
27242776 targets: persistedTargets,
2777+ localScopeState: input.localScopeState,
27252778 });
27262779
27272780 if (providerAuthResult.kind === "connected") {
@@ -2749,10 +2802,10 @@ const connectGoogleDiscoveryBatchInternal = (input: { · 21 unmodified lines
27492802
27502803 type RuntimeSourceAuthServiceShape = {
27512804 getSourceById: (input: {
2752− workspaceId: WorkspaceId;
2805+ scopeId: ScopeId;
27532806 sourceId: Source["id"];
2754− actorAccountId?: AccountId | null;
2755− }) => Effect.Effect<Source, Error, WorkspaceStorageServices>;
2807+ actorScopeId?: ScopeId | null;
2808+ }) => Effect.Effect<Source, Error, ScopeStorageServices>;
27562809 getLocalServerBaseUrl: () => string | null;
27572810 storeSecretMaterial: (input: {
27582811 purpose: SecretMaterialPurpose;
@@ -2764,28 +2817,28 @@ type RuntimeSourceAuthServiceShape = { · 5 unmodified lines
27642817 mcpDiscoveryElicitation?: McpDiscoveryElicitationContext;
27652818 baseUrl?: string | null;
27662819 },
2767− ) => Effect.Effect<ExecutorSourceAddResult, Error, WorkspaceStorageServices>;
2820+ ) => Effect.Effect<ExecutorSourceAddResult, Error, ScopeStorageServices>;
27682821 connectGoogleDiscoveryBatch: (
27692822 input: ConnectGoogleDiscoveryBatchInput,
2770− ) => Effect.Effect<ConnectGoogleDiscoveryBatchResult, Error, WorkspaceStorageServices>;
2823+ ) => Effect.Effect<ConnectGoogleDiscoveryBatchResult, Error, ScopeStorageServices>;
27712824 connectMcpSource: (
27722825 input: ConnectMcpSourceInput,
2773− ) => Effect.Effect<McpSourceConnectResult, Error, WorkspaceStorageServices>;
2774− listWorkspaceOauthClients: (input: {
2775− workspaceId: WorkspaceId;
2826+ ) => Effect.Effect<McpSourceConnectResult, Error, ScopeStorageServices>;
2827+ listScopeOauthClients: (input: {
2828+ scopeId: ScopeId;
27762829 providerKey: string;
2777− }) => Effect.Effect<readonly WorkspaceOauthClient[], Error, WorkspaceStorageServices>;
2778− createWorkspaceOauthClient: (
2779− input: CreateWorkspaceOauthClientInput,
2780− ) => Effect.Effect<WorkspaceOauthClient, Error, WorkspaceStorageServices>;
2781− removeWorkspaceOauthClient: (input: {
2782− workspaceId: WorkspaceId;
2783− oauthClientId: WorkspaceOauthClient["id"];
2784− }) => Effect.Effect<boolean, Error, WorkspaceStorageServices>;
2830+ }) => Effect.Effect<readonly ScopeOauthClient[], Error, ScopeStorageServices>;
2831+ createScopeOauthClient: (
2832+ input: CreateScopeOauthClientInput,
2833+ ) => Effect.Effect<ScopeOauthClient, Error, ScopeStorageServices>;
2834+ removeScopeOauthClient: (input: {
2835+ scopeId: ScopeId;
2836+ oauthClientId: ScopeOauthClient["id"];
2837+ }) => Effect.Effect<boolean, Error, ScopeStorageServices>;
27852838 removeProviderAuthGrant: (input: {
2786− workspaceId: WorkspaceId;
2839+ scopeId: ScopeId;
27872840 grantId: ProviderAuthGrant["id"];
2788− }) => Effect.Effect<boolean, Error, WorkspaceStorageServices>;
2841+ }) => Effect.Effect<boolean, Error, ScopeStorageServices>;
27892842 startSourceOAuthSession: (
27902843 input: StartSourceOAuthSessionInput,
27912844 ) => Effect.Effect<StartSourceOAuthSessionResult, Error, never>;
@@ -2794,24 +2847,24 @@ type RuntimeSourceAuthServiceShape = { · 2 unmodified lines
27942847 code?: string | null;
27952848 error?: string | null;
27962849 errorDescription?: string | null;
2797− }) => Effect.Effect<CompleteSourceOAuthSessionResult, Error, WorkspaceStorageServices>;
2850+ }) => Effect.Effect<CompleteSourceOAuthSessionResult, Error, ScopeStorageServices>;
27982851 completeProviderOauthCallback: (input: {
2799− workspaceId: WorkspaceId;
2800− actorAccountId?: AccountId | null;
2852+ scopeId: ScopeId;
2853+ actorScopeId?: ScopeId | null;
28012854 state: string;
28022855 code?: string | null;
28032856 error?: string | null;
28042857 errorDescription?: string | null;
2805− }) => Effect.Effect<CompleteProviderOauthCallbackResult, Error, WorkspaceStorageServices>;
2858+ }) => Effect.Effect<CompleteProviderOauthCallbackResult, Error, ScopeStorageServices>;
28062859 completeSourceCredentialSetup: (input: {
2807− workspaceId: WorkspaceId;
2860+ scopeId: ScopeId;
28082861 sourceId: Source["id"];
2809− actorAccountId?: AccountId | null;
2862+ actorScopeId?: ScopeId | null;
28102863 state: string;
28112864 code?: string | null;
28122865 error?: string | null;
28132866 errorDescription?: string | null;
2814− }) => Effect.Effect<CompleteSourceCredentialSetupResult, Error, WorkspaceStorageServices>;
2867+ }) => Effect.Effect<CompleteSourceCredentialSetupResult, Error, ScopeStorageServices>;
28152868 };
28162869
28172870 type RuntimeSourceAuthDependencies = {
@@ -2823,10 +2876,10 @@ type RuntimeSourceAuthDependencies = { · 5 unmodified lines
28232876 storeSecretMaterial: StoreSecretMaterial;
28242877 deleteSecretMaterial: DeleteSecretMaterial;
28252878 getLocalServerBaseUrl?: () => string | undefined;
2826− localWorkspaceState?: RuntimeLocalWorkspaceState;
2879+ localScopeState?: RuntimeLocalScopeState;
28272880 };
28282881
2829−type ProvideLocalWorkspace = <A, E, R>(
2882+type ProvideLocalScope = <A, E, R>(
28302883 effect: Effect.Effect<A, E, R>,
28312884 ) => Effect.Effect<A, E, R>;
28322885
@@ -2836,9 +2889,9 @@ type RuntimeSourceConnectionServiceShape = Pick< · 3 unmodified lines
28362889 | "addExecutorSource"
28372890 | "connectGoogleDiscoveryBatch"
28382891 | "connectMcpSource"
2839− | "listWorkspaceOauthClients"
2840− | "createWorkspaceOauthClient"
2841− | "removeWorkspaceOauthClient"
2892+ | "listScopeOauthClients"
2893+ | "createScopeOauthClient"
2894+ | "removeScopeOauthClient"
28422895 | "removeProviderAuthGrant"
28432896 >;
28442897
@@ -2852,24 +2905,24 @@ type RuntimeSourceOAuthSessionServiceShape = Pick< · 7 unmodified lines
28522905
28532906 const createRuntimeSourceConnectionService = (
28542907 input: RuntimeSourceAuthDependencies,
2855− provideLocalWorkspace: ProvideLocalWorkspace,
2908+ provideLocalWorkspace: ProvideLocalScope,
28562909 ): RuntimeSourceConnectionServiceShape => {
28572910 const mirrorLocalSourceResult = (
28582911 result: ExecutorSourceAddResult,
2859− ): Effect.Effect<ExecutorSourceAddResult, Error, WorkspaceStorageServices> =>
2912+ ): Effect.Effect<ExecutorSourceAddResult, Error, ScopeStorageServices> =>
28602913 Effect.succeed(result);
28612914 const mirrorLocalMcpSourceResult = (
28622915 result: McpSourceConnectResult,
2863− ): Effect.Effect<McpSourceConnectResult, Error, WorkspaceStorageServices> =>
2916+ ): Effect.Effect<McpSourceConnectResult, Error, ScopeStorageServices> =>
28642917 Effect.succeed(result);
28652918
28662919 return {
2867− getSourceById: ({ workspaceId, sourceId, actorAccountId }) =>
2920+ getSourceById: ({ scopeId, sourceId, actorScopeId }) =>
28682921 provideLocalWorkspace(
28692922 input.sourceStore.loadSourceById({
2870− workspaceId,
2923+ scopeId,
28712924 sourceId,
2872− actorAccountId,
2925+ actorScopeId,
28732926 }),
28742927 ),
28752928
@@ -2886,6 +2939,7 @@ const createRuntimeSourceConnectionService = ( · 10 unmodified lines
28862939 resolveSecretMaterial: input.resolveSecretMaterial,
28872940 getLocalServerBaseUrl: input.getLocalServerBaseUrl,
28882941 baseUrl: options?.baseUrl,
2942+ localScopeState: input.localScopeState,
28892943 })
28902944 : isExecutorHttpEndpointSourceInput(sourceInput)
28912945 ? addExecutorHttpSource({
@@ -2898,6 +2952,7 @@ const createRuntimeSourceConnectionService = ( · 6 unmodified lines
28982952 resolveSecretMaterial: input.resolveSecretMaterial,
28992953 getLocalServerBaseUrl: input.getLocalServerBaseUrl,
29002954 baseUrl: options?.baseUrl,
2955+ localScopeState: input.localScopeState,
29012956 })
29022957 : isExecutorMcpSourceInput(sourceInput)
29032958 ? connectMcpSourceInternal({
@@ -2905,8 +2960,8 @@ const createRuntimeSourceConnectionService = ( · 1 unmodified line
29052960 sourceStore: input.sourceStore,
29062961 sourceCatalogSync: input.sourceCatalogSync,
29072962 getLocalServerBaseUrl: input.getLocalServerBaseUrl,
2908− workspaceId: sourceInput.workspaceId,
2909− actorAccountId: sourceInput.actorAccountId,
2963+ scopeId: sourceInput.scopeId,
2964+ actorScopeId: sourceInput.actorScopeId,
29102965 executionId: sourceInput.executionId,
29112966 interactionId: sourceInput.interactionId,
29122967 endpoint: sourceInput.endpoint,
@@ -2936,6 +2991,7 @@ const createRuntimeSourceConnectionService = ( · 23 unmodified lines
29362991 sourceCatalogSync: input.sourceCatalogSync,
29372992 sourceInput,
29382993 getLocalServerBaseUrl: input.getLocalServerBaseUrl,
2994+ localScopeState: input.localScopeState,
29392995 }),
29402996 ),
29412997
@@ -2946,8 +3002,8 @@ const createRuntimeSourceConnectionService = ( · 4 unmodified lines
29463002 sourceStore: input.sourceStore,
29473003 sourceCatalogSync: input.sourceCatalogSync,
29483004 getLocalServerBaseUrl: input.getLocalServerBaseUrl,
2949− workspaceId: sourceInput.workspaceId,
2950− actorAccountId: sourceInput.actorAccountId,
3005+ scopeId: sourceInput.scopeId,
3006+ actorScopeId: sourceInput.actorScopeId,
29513007 sourceId: sourceInput.sourceId,
29523008 executionId: null,
29533009 interactionId: null,
@@ -2969,54 +3025,71 @@ const createRuntimeSourceConnectionService = ( · 15 unmodified lines
29693025 ),
29703026 ),
29713027
2972− listWorkspaceOauthClients: ({ workspaceId, providerKey }) =>
3028+ listScopeOauthClients: ({ scopeId, providerKey }) =>
29733029 provideLocalWorkspace(
2974− input.executorState.workspaceOauthClients.listByWorkspaceAndProvider({
2975− workspaceId,
2976− providerKey,
3030+ Effect.gen(function* () {
3031+ const scopeIds = resolvedScopeSearchOrder({
3032+ scopeId,
3033+ localScopeState: input.localScopeState,
3034+ });
3035+ const clientsById = new Map<ScopeOauthClient["id"], ScopeOauthClient>();
3036+
3037+ for (const candidateScopeId of scopeIds) {
3038+ const clients = yield* input.executorState.scopeOauthClients.listByScopeAndProvider({
3039+ scopeId: candidateScopeId,
3040+ providerKey,
3041+ });
3042+ for (const client of clients) {
3043+ if (!clientsById.has(client.id)) {
3044+ clientsById.set(client.id, client);
3045+ }
3046+ }
3047+ }
3048+
3049+ return [...clientsById.values()];
29773050 }),
29783051 ),
29793052
2980− createWorkspaceOauthClient: ({ workspaceId, providerKey, label, oauthClient }) =>
3053+ createScopeOauthClient: ({ scopeId, providerKey, label, oauthClient }) =>
29813054 provideLocalWorkspace(
29823055 Effect.gen(function* () {
29833056 const sourceAdapter = findSourceAdapterByProviderKey(providerKey);
2984− const created = yield* createWorkspaceOauthClient({
3057+ const created = yield* createScopeOauthClient({
29853058 executorState: input.executorState,
2986− workspaceId,
3059+ scopeId,
29873060 providerKey,
29883061 oauthClient,
29893062 label,
29903063 normalizeOauthClient: sourceAdapter?.normalizeOauthClientInput,
29913064 storeSecretMaterial: input.storeSecretMaterial,
29923065 });
2993− const stored = yield* input.executorState.workspaceOauthClients.getById(created.id);
3066+ const stored = yield* input.executorState.scopeOauthClients.getById(created.id);
29943067 if (Option.isNone(stored)) {
2995− return yield* runtimeEffectError("sources/source-auth-service", `Workspace OAuth client ${created.id} was not persisted`);
3068+ return yield* runtimeEffectError("sources/source-auth-service", `Scope OAuth client ${created.id} was not persisted`);
29963069 }
29973070
29983071 return stored.value;
29993072 }),
30003073 ),
30013074
3002− removeWorkspaceOauthClient: ({ workspaceId, oauthClientId }) =>
3075+ removeScopeOauthClient: ({ scopeId, oauthClientId }) =>
30033076 provideLocalWorkspace(
30043077 Effect.gen(function* () {
3005− const oauthClient = yield* input.executorState.workspaceOauthClients.getById(oauthClientId);
3006− if (Option.isNone(oauthClient) || oauthClient.value.workspaceId !== workspaceId) {
3078+ const oauthClient = yield* input.executorState.scopeOauthClients.getById(oauthClientId);
3079+ if (Option.isNone(oauthClient) || oauthClient.value.scopeId !== scopeId) {
30073080 return false;
30083081 }
30093082
3010− const grants = yield* input.executorState.providerAuthGrants.listByWorkspaceId(workspaceId);
3083+ const grants = yield* input.executorState.providerAuthGrants.listByScopeId(scopeId);
30113084 const dependentGrant = grants.find((grant) => grant.oauthClientId === oauthClientId);
30123085 if (dependentGrant) {
30133086 return yield* runtimeEffectError("sources/source-auth-service",
3014− `Workspace OAuth client ${oauthClientId} is still referenced by provider grant ${dependentGrant.id}`,
3087+ `Scope OAuth client ${oauthClientId} is still referenced by provider grant ${dependentGrant.id}`,
30153088 );
30163089 }
30173090
30183091 const secretRef = sourceOauthClientSecretRef(oauthClient.value);
3019− const removed = yield* input.executorState.workspaceOauthClients.removeById(oauthClientId);
3092+ const removed = yield* input.executorState.scopeOauthClients.removeById(oauthClientId);
30203093 if (removed && secretRef) {
30213094 yield* input.deleteSecretMaterial(secretRef).pipe(
30223095 Effect.either,
@@ -3028,12 +3101,12 @@ const createRuntimeSourceConnectionService = ( · 5 unmodified lines
30283101 }),
30293102 ),
30303103
3031− removeProviderAuthGrant: ({ workspaceId, grantId }) =>
3104+ removeProviderAuthGrant: ({ scopeId, grantId }) =>
30323105 provideLocalWorkspace(
30333106 removeProviderAuthGrantInternal({
30343107 executorState: input.executorState,
30353108 sourceStore: input.sourceStore,
3036− workspaceId,
3109+ scopeId,
30373110 grantId,
30383111 deleteSecretMaterial: input.deleteSecretMaterial,
30393112 }),
@@ -3043,7 +3116,7 @@ const createRuntimeSourceConnectionService = ( · 3 unmodified lines
30433116
30443117 const createRuntimeSourceOAuthSessionService = (
30453118 input: RuntimeSourceAuthDependencies,
3046− provideLocalWorkspace: ProvideLocalWorkspace,
3119+ provideLocalWorkspace: ProvideLocalScope,
30473120 ): RuntimeSourceOAuthSessionServiceShape => ({
30483121 startSourceOAuthSession: (oauthInput) =>
30493122 Effect.gen(function* () {
@@ -3069,9 +3142,9 @@ const createRuntimeSourceOAuthSessionService = ( · 19 unmodified lines
30693142
30703143 yield* input.executorState.sourceAuthSessions.upsert({
30713144 id: sessionId,
3072− workspaceId: oauthInput.workspaceId,
3145+ scopeId: oauthInput.scopeId,
30733146 sourceId: SourceIdSchema.make(`oauth_draft_${crypto.randomUUID()}`),
3074− actorAccountId: oauthInput.actorAccountId ?? null,
3147+ actorScopeId: oauthInput.actorScopeId ?? null,
30753148 credentialSlot: "runtime",
30763149 executionId: null,
30773150 interactionId: null,
@@ -3215,7 +3288,7 @@ const createRuntimeSourceOAuthSessionService = ( · 137 unmodified lines
32153288 } satisfies CompleteSourceOAuthSessionResult;
32163289 })),
32173290
3218− completeProviderOauthCallback: ({ workspaceId, actorAccountId, state, code, error, errorDescription }) =>
3291+ completeProviderOauthCallback: ({ scopeId, actorScopeId, state, code, error, errorDescription }) =>
32193292 provideLocalWorkspace(Effect.gen(function* () {
32203293 const sessionOption = yield* input.executorState.sourceAuthSessions.getByState(state);
32213294 if (Option.isNone(sessionOption)) {
@@ -3223,10 +3296,10 @@ const createRuntimeSourceOAuthSessionService = ( · 1 unmodified line
32233296 }
32243297
32253298 const session = sessionOption.value;
3226− if (session.workspaceId !== workspaceId) {
3227− return yield* runtimeEffectError("sources/source-auth-service", `Source auth session ${session.id} does not match workspaceId=${workspaceId}`);
3299+ if (session.scopeId !== scopeId) {
3300+ return yield* runtimeEffectError("sources/source-auth-service", `Source auth session ${session.id} does not match scopeId=${scopeId}`);
32283301 }
3229− if ((session.actorAccountId ?? null) !== (actorAccountId ?? null)) {
3302+ if ((session.actorScopeId ?? null) !== (actorScopeId ?? null)) {
32303303 return yield* runtimeEffectError("sources/source-auth-service", `Source auth session ${session.id} does not match the active account`);
32313304 }
32323305 if (session.providerKind !== "oauth2_provider_batch") {
@@ -3238,9 +3311,9 @@ const createRuntimeSourceOAuthSessionService = ( · 5 unmodified lines
32383311 sessionData.targetSources,
32393312 (target) =>
32403313 input.sourceStore.loadSourceById({
3241− workspaceId,
3314+ scopeId,
32423315 sourceId: target.sourceId,
3243− actorAccountId,
3316+ actorScopeId,
32443317 }),
32453318 { discard: false },
32463319 );
@@ -3276,14 +3349,15 @@ const createRuntimeSourceOAuthSessionService = ( · 29 unmodified lines
32763349 return yield* runtimeEffectError("sources/source-auth-service", "OAuth session is missing the PKCE code verifier");
32773350 }
32783351
3279− const oauthClient = yield* resolveWorkspaceOauthClientById({
3352+ const oauthClient = yield* resolveScopeOauthClientById({
32803353 executorState: input.executorState,
3281− workspaceId,
3354+ scopeId,
32823355 oauthClientId: sessionData.oauthClientId,
32833356 providerKey: sessionData.providerKey,
3357+ localScopeState: input.localScopeState,
32843358 });
32853359 if (oauthClient === null) {
3286− return yield* runtimeEffectError("sources/source-auth-service", `Workspace OAuth client not found: ${sessionData.oauthClientId}`);
3360+ return yield* runtimeEffectError("sources/source-auth-service", `Scope OAuth client not found: ${sessionData.oauthClientId}`);
32873361 }
32883362
32893363 let clientSecret: string | null = null;
@@ -3303,9 +3377,9 @@ const createRuntimeSourceOAuthSessionService = ( · 13 unmodified lines
33033377 code: authorizationCode,
33043378 });
33053379
3306− const availableGrants = yield* input.executorState.providerAuthGrants.listByWorkspaceActorAndProvider({
3307− workspaceId,
3308− actorAccountId: actorAccountId ?? null,
3380+ const availableGrants = yield* input.executorState.providerAuthGrants.listByScopeActorAndProvider({
3381+ scopeId,
3382+ actorScopeId: actorScopeId ?? null,
33093383 providerKey: sessionData.providerKey,
33103384 });
33113385 const existingGrant = availableGrants.find(
@@ -3319,8 +3393,8 @@ const createRuntimeSourceOAuthSessionService = ( · 7 unmodified lines
33193393 );
33203394 const nextGrant = yield* upsertProviderAuthGrant({
33213395 executorState: input.executorState,
3322− workspaceId,
3323− actorAccountId,
3396+ scopeId,
3397+ actorScopeId,
33243398 providerKey: sessionData.providerKey,
33253399 oauthClientId: sessionData.oauthClientId,
33263400 tokenEndpoint: sessionData.tokenEndpoint,
@@ -3339,9 +3413,9 @@ const createRuntimeSourceOAuthSessionService = ( · 12 unmodified lines
33393413 (target) =>
33403414 Effect.map(
33413415 input.sourceStore.loadSourceById({
3342− workspaceId,
3416+ scopeId,
33433417 sourceId: target.sourceId,
3344− actorAccountId,
3418+ actorScopeId,
33453419 }),
33463420 (source) => ({
33473421 source,
@@ -3354,7 +3428,7 @@ const createRuntimeSourceOAuthSessionService = ( · 6 unmodified lines
33543428 const connectedSources = yield* attachProviderGrantToSources({
33553429 sourceStore: input.sourceStore,
33563430 sourceCatalogSync: input.sourceCatalogSync,
3357− actorAccountId,
3431+ actorScopeId,
33583432 grantId: nextGrant.id,
33593433 providerKey: nextGrant.providerKey,
33603434 headerName: nextGrant.headerName,
@@ -3384,7 +3458,7 @@ const createRuntimeSourceOAuthSessionService = ( · 23 unmodified lines
33843458 } satisfies CompleteProviderOauthCallbackResult;
33853459 })),
33863460
3387− completeSourceCredentialSetup: ({ workspaceId, sourceId, actorAccountId, state, code, error, errorDescription }) =>
3461+ completeSourceCredentialSetup: ({ scopeId, sourceId, actorScopeId, state, code, error, errorDescription }) =>
33883462 provideLocalWorkspace(Effect.gen(function* () {
33893463 const sessionOption = yield* input.executorState.sourceAuthSessions.getByState(state);
33903464 if (Option.isNone(sessionOption)) {
@@ -3392,22 +3466,22 @@ const createRuntimeSourceOAuthSessionService = ( · 1 unmodified line
33923466 }
33933467
33943468 const session = sessionOption.value;
3395− if (session.workspaceId !== workspaceId || session.sourceId !== sourceId) {
3469+ if (session.scopeId !== scopeId || session.sourceId !== sourceId) {
33963470 return yield* runtimeEffectError("sources/source-auth-service",
3397− `Source auth session ${session.id} does not match workspaceId=${workspaceId} sourceId=${sourceId}`,
3471+ `Source auth session ${session.id} does not match scopeId=${scopeId} sourceId=${sourceId}`,
33983472 );
33993473 }
34003474 if (
3401− actorAccountId !== undefined
3402− && (session.actorAccountId ?? null) !== (actorAccountId ?? null)
3475+ actorScopeId !== undefined
3476+ && (session.actorScopeId ?? null) !== (actorScopeId ?? null)
34033477 ) {
34043478 return yield* runtimeEffectError("sources/source-auth-service", `Source auth session ${session.id} does not match the active account`);
34053479 }
34063480
34073481 const source = yield* input.sourceStore.loadSourceById({
3408− workspaceId: session.workspaceId,
3482+ scopeId: session.scopeId,
34093483 sourceId: session.sourceId,
3410− actorAccountId: session.actorAccountId,
3484+ actorScopeId: session.actorScopeId,
34113485 });
34123486
34133487 if (session.status === "completed") {
@@ -3439,13 +3513,13 @@ const createRuntimeSourceOAuthSessionService = ( · 25 unmodified lines
34393513 }),
34403514 );
34413515 const failedSource = yield* updateSourceStatus(input.sourceStore, source, {
3442− actorAccountId: session.actorAccountId,
3516+ actorScopeId: session.actorScopeId,
34433517 status: "error",
34443518 lastError: reason,
34453519 });
34463520 yield* input.sourceCatalogSync.sync({
34473521 source: failedSource,
3448− actorAccountId: session.actorAccountId,
3522+ actorScopeId: session.actorScopeId,
34493523 });
34503524 yield* completeLiveInteraction({
34513525 executorState: input.executorState,
@@ -3503,7 +3577,7 @@ const createRuntimeSourceOAuthSessionService = ( · 51 unmodified lines
35033577 ? exchanged.scope!.split(/\s+/).filter((scope) => scope.length > 0)
35043578 : [...oauthSessionData.scopes];
35053579 connectedSource = yield* updateSourceStatus(input.sourceStore, source, {
3506− actorAccountId: session.actorAccountId,
3580+ actorScopeId: session.actorScopeId,
35073581 status: "connected",
35083582 lastError: null,
35093583 auth: {
@@ -3518,10 +3592,10 @@ const createRuntimeSourceOAuthSessionService = ( · 8 unmodified lines
35183592 grantSet: grantedScopes,
35193593 },
35203594 });
3521− const authArtifact = yield* input.executorState.authArtifacts.getByWorkspaceSourceAndActor({
3522− workspaceId: connectedSource.workspaceId,
3595+ const authArtifact = yield* input.executorState.authArtifacts.getByScopeSourceAndActor({
3596+ scopeId: connectedSource.scopeId,
35233597 sourceId: connectedSource.id,
3524− actorAccountId: session.actorAccountId ?? null,
3598+ actorScopeId: session.actorScopeId ?? null,
35253599 slot: "runtime",
35263600 });
35273601 if (Option.isSome(authArtifact)) {
@@ -3583,7 +3657,7 @@ const createRuntimeSourceOAuthSessionService = ( · 55 unmodified lines
35833657 : null;
35843658
35853659 connectedSource = yield* updateSourceStatus(input.sourceStore, source, {
3586− actorAccountId: session.actorAccountId,
3660+ actorScopeId: session.actorScopeId,
35873661 status: "connected",
35883662 lastError: null,
35893663 auth: createPersistedMcpOAuthSourceAuth({
@@ -3628,13 +3702,13 @@ const createRuntimeSourceOAuthSessionService = ( · 38 unmodified lines
36283702 const indexed = yield* Effect.either(
36293703 input.sourceCatalogSync.sync({
36303704 source: connectedSource,
3631− actorAccountId: session.actorAccountId,
3705+ actorScopeId: session.actorScopeId,
36323706 }),
36333707 );
36343708 yield* Either.match(indexed, {
36353709 onLeft: (error) =>
36363710 updateSourceStatus(input.sourceStore, connectedSource, {
3637− actorAccountId: session.actorAccountId,
3711+ actorScopeId: session.actorScopeId,
36383712 status: "error",
36393713 lastError: error.message,
36403714 }).pipe(
@@ -3660,8 +3734,8 @@ const createRuntimeSourceOAuthSessionService = ( · 19 unmodified lines
36603734 });
36613735
36623736 export const createRuntimeSourceAuthService = (input: RuntimeSourceAuthDependencies) => {
3663− const provideLocalWorkspace: ProvideLocalWorkspace = (effect) =>
3664− provideOptionalRuntimeLocalWorkspace(effect, input.localWorkspaceState);
3737+ const provideLocalWorkspace: ProvideLocalScope = (effect) =>
3738+ provideOptionalRuntimeLocalScope(effect, input.localScopeState);
36653739 const sourceConnection = createRuntimeSourceConnectionService(
36663740 input,
36673741 provideLocalWorkspace,
@@ -3704,7 +3778,7 @@ export const RuntimeSourceAuthServiceLive = (input: { · 36 unmodified lines
37043778 const resolveSecretMaterial = yield* SecretMaterialResolverService;
37053779 const storeSecretMaterial = yield* SecretMaterialStorerService;
37063780 const deleteSecretMaterial = yield* SecretMaterialDeleterService;
3707− const runtimeLocalWorkspace = yield* getRuntimeLocalWorkspaceOption();
3781+ const runtimeLocalScope = yield* getRuntimeLocalScopeOption();
37083782
37093783 return createRuntimeSourceAuthService({
37103784 executorState,
@@ -3715,7 +3789,7 @@ export const RuntimeSourceAuthServiceLive = (input: { · 4 unmodified lines
37153789 storeSecretMaterial,
37163790 deleteSecretMaterial,
37173791 getLocalServerBaseUrl: input.getLocalServerBaseUrl,
3718− localWorkspaceState: runtimeLocalWorkspace ?? undefined,
3792+ localScopeState: runtimeLocalScope ?? undefined,
37193793 });
37203794 }),
37213795 );
packages/platform/sdk/src/runtime/sources/source-credential-interactions.ts+4−1
@@ -1,4 +1,7 @@
1−import { SecretRefSchema, type SecretRef } from "#schema";
1+import {
2+ SecretRefSchema,
3+ type SecretRef,
4+} from "#schema";
25 import * as Schema from "effect/Schema";
36
47 export const SourceCredentialSelectionContentSchema = Schema.Union(
packages/platform/sdk/src/runtime/sources/source-definitions.test.ts+20−14
@@ -1,11 +1,15 @@
1−import { describe, expect, it } from "@effect/vitest";
1+import {
2+ describe,
3+ expect,
4+ it,
5+} from "@effect/vitest";
26 import * as Effect from "effect/Effect";
37
48 import {
59 CredentialIdSchema,
610 decodeBuiltInAuthArtifactConfig,
711 SourceIdSchema,
8− WorkspaceIdSchema,
12+ ScopeIdSchema,
913 type Source,
1014 } from "#schema";
1115
@@ -17,7 +21,9 @@ import { · 5 unmodified lines
1721 stableSourceCatalogRevisionId,
1822 updateSourceFromPayload,
1923 } from "./source-definitions";
20−import { namespaceFromSourceName } from "./source-names";
24+import {
25+ namespaceFromSourceName,
26+} from "./source-names";
2127
2228 const openApiBinding = (
2329 specUrl = "https://api.github.com/openapi.json",
@@ -81,7 +87,7 @@ const mcpBinding = ( · 57 unmodified lines
8187
8288 const makeSource = (overrides: Partial<Source> = {}): Source => ({
8389 id: SourceIdSchema.make("src_source_definitions"),
84− workspaceId: WorkspaceIdSchema.make("ws_source_definitions"),
90+ scopeId: ScopeIdSchema.make("ws_source_definitions"),
8591 name: "GitHub",
8692 kind: "openapi",
8793 endpoint: "https://api.github.com",
@@ -108,7 +114,7 @@ describe("source-definitions", () => { · 20 unmodified lines
108114 name: "Renamed GitHub",
109115 });
110116 const differentWorkspace = makeSource({
111− workspaceId: WorkspaceIdSchema.make("ws_source_definitions_other"),
117+ scopeId: ScopeIdSchema.make("ws_source_definitions_other"),
112118 });
113119
114120 expect(stableSourceCatalogId(source)).toBe(stableSourceCatalogId(source));
@@ -151,7 +157,7 @@ describe("source-definitions", () => { · 36 unmodified lines
151157 () =>
152158 Effect.gen(function* () {
153159 const source = yield* createSourceFromPayload({
154− workspaceId: WorkspaceIdSchema.make("ws_create_defaults"),
160+ scopeId: ScopeIdSchema.make("ws_create_defaults"),
155161 sourceId: SourceIdSchema.make("src_create_defaults"),
156162 payload: {
157163 name: " GitHub ",
@@ -216,7 +222,7 @@ describe("source-definitions", () => { · 58 unmodified lines
216222 () =>
217223 Effect.gen(function* () {
218224 const source = yield* createSourceFromPayload({
219− workspaceId: WorkspaceIdSchema.make("ws_create_oauth_defaults"),
225+ scopeId: ScopeIdSchema.make("ws_create_oauth_defaults"),
220226 sourceId: SourceIdSchema.make("src_create_oauth_defaults"),
221227 payload: {
222228 name: "GraphQL Demo",
@@ -253,7 +259,7 @@ describe("source-definitions", () => { · 30 unmodified lines
253259 Effect.gen(function* () {
254260 const invalidBearer = yield* Effect.flip(
255261 createSourceFromPayload({
256− workspaceId: WorkspaceIdSchema.make("ws_invalid_bearer"),
262+ scopeId: ScopeIdSchema.make("ws_invalid_bearer"),
257263 sourceId: SourceIdSchema.make("src_invalid_bearer"),
258264 payload: {
259265 name: "Bad Bearer",
@@ -279,7 +285,7 @@ describe("source-definitions", () => { · 19 unmodified lines
279285
280286 const invalidRefresh = yield* Effect.flip(
281287 createSourceFromPayload({
282− workspaceId: WorkspaceIdSchema.make("ws_invalid_refresh"),
288+ scopeId: ScopeIdSchema.make("ws_invalid_refresh"),
283289 sourceId: SourceIdSchema.make("src_invalid_refresh"),
284290 payload: {
285291 name: "Bad OAuth",
@@ -313,7 +319,7 @@ describe("source-definitions", () => { · 27 unmodified lines
313319 Effect.gen(function* () {
314320 const invalidMcp = yield* Effect.flip(
315321 createSourceFromPayload({
316− workspaceId: WorkspaceIdSchema.make("ws_invalid_mcp"),
322+ scopeId: ScopeIdSchema.make("ws_invalid_mcp"),
317323 sourceId: SourceIdSchema.make("src_invalid_mcp"),
318324 payload: {
319325 name: "MCP",
@@ -332,7 +338,7 @@ describe("source-definitions", () => { · 12 unmodified lines
332338
333339 const invalidOpenApiSpec = yield* Effect.flip(
334340 createSourceFromPayload({
335− workspaceId: WorkspaceIdSchema.make("ws_invalid_openapi_spec"),
341+ scopeId: ScopeIdSchema.make("ws_invalid_openapi_spec"),
336342 sourceId: SourceIdSchema.make("src_invalid_openapi_spec"),
337343 payload: {
338344 name: "OpenAPI",
@@ -349,7 +355,7 @@ describe("source-definitions", () => { · 10 unmodified lines
349355
350356 const invalidOpenApiTransport = yield* Effect.flip(
351357 createSourceFromPayload({
352− workspaceId: WorkspaceIdSchema.make("ws_invalid_openapi_transport"),
358+ scopeId: ScopeIdSchema.make("ws_invalid_openapi_transport"),
353359 sourceId: SourceIdSchema.make("src_invalid_openapi_transport"),
354360 payload: {
355361 name: "OpenAPI",
@@ -369,7 +375,7 @@ describe("source-definitions", () => { · 13 unmodified lines
369375
370376 const invalidGraphql = yield* Effect.flip(
371377 createSourceFromPayload({
372− workspaceId: WorkspaceIdSchema.make("ws_invalid_graphql"),
378+ scopeId: ScopeIdSchema.make("ws_invalid_graphql"),
373379 sourceId: SourceIdSchema.make("src_invalid_graphql"),
374380 payload: {
375381 name: "GraphQL",
@@ -388,7 +394,7 @@ describe("source-definitions", () => { · 12 unmodified lines
388394
389395 const invalidInternal = yield* Effect.flip(
390396 createSourceFromPayload({
391− workspaceId: WorkspaceIdSchema.make("ws_invalid_internal"),
397+ scopeId: ScopeIdSchema.make("ws_invalid_internal"),
392398 sourceId: SourceIdSchema.make("src_invalid_internal"),
393399 payload: {
394400 name: "Internal",
packages/platform/sdk/src/runtime/sources/source-definitions.ts+20−14
@@ -1,11 +1,13 @@
1−import { sha256Hex } from "@executor/codemode-core";
1+import {
2+ sha256Hex,
3+} from "@executor/codemode-core";
24
35 import type {
46 CreateSourcePayload,
57 UpdateSourcePayload,
68 } from "../../sources/contracts";
79 import type {
8− AccountId,
10+ ScopeId,
911 AuthArtifact,
1012 Source,
1113 SourceAuth,
@@ -17,7 +19,6 @@ import type { · 5 unmodified lines
1719 StoredSourceRecord,
1820 StoredSourceCatalogRecord,
1921 StoredSourceCatalogRevisionRecord,
20− WorkspaceId,
2122 } from "#schema";
2223 import {
2324 AuthArtifactIdSchema,
@@ -27,12 +28,17 @@ import { · 3 unmodified lines
2728 } from "#schema";
2829 import * as Effect from "effect/Effect";
2930
30−import { getSourceAdapter, getSourceAdapterForSource } from "./source-adapters";
31+import {
32+ getSourceAdapter,
33+ getSourceAdapterForSource,
34+} from "./source-adapters";
3135 import {
3236 authArtifactFromSourceAuth,
3337 sourceAuthFromAuthArtifact,
3438 } from "../auth/auth-artifacts";
35−import { runtimeEffectError } from "../effect-errors";
39+import {
40+ runtimeEffectError,
41+} from "../effect-errors";
3642
3743 const trimOrNull = (value: string | null | undefined): string | null => {
3844 if (value === null || value === undefined) {
@@ -277,7 +283,7 @@ const validateSourceByKind = (source: Source): Effect.Effect<Source, Error, neve · 238 unmodified lines
277283 );
278284
279285 export const createSourceFromPayload = (input: {
280− workspaceId: WorkspaceId;
286+ scopeId: ScopeId;
281287 sourceId: Source["id"];
282288 payload: CreateSourcePayload;
283289 now: number;
@@ -292,7 +298,7 @@ export const createSourceFromPayload = (input: { · 8 unmodified lines
292298
293299 return yield* validateSourceByKind({
294300 id: input.sourceId,
295− workspaceId: input.workspaceId,
301+ scopeId: input.scopeId,
296302 name: input.payload.name.trim(),
297303 kind: input.payload.kind,
298304 endpoint: input.payload.endpoint.trim(),
@@ -370,7 +376,7 @@ export const createSourceCatalogRecord = (input: { · 71 unmodified lines
370376 providerKey: sourceCatalogProviderKeyFromSource(input.source),
371377 name: input.source.name,
372378 summary: null,
373− visibility: "workspace",
379+ visibility: "scope",
374380 latestRevisionId: input.latestRevisionId,
375381 createdAt: input.source.createdAt,
376382 updatedAt: input.source.updatedAt,
@@ -402,7 +408,7 @@ export const splitSourceForStorage = (input: { · 25 unmodified lines
402408 source: Source;
403409 catalogId: SourceCatalogId;
404410 catalogRevisionId: SourceCatalogRevisionId;
405− actorAccountId?: AccountId | null;
411+ actorScopeId?: ScopeId | null;
406412 existingRuntimeAuthArtifactId?: AuthArtifact["id"] | null;
407413 existingImportAuthArtifactId?: AuthArtifact["id"] | null;
408414 }): {
@@ -412,7 +418,7 @@ export const splitSourceForStorage = (input: { · 3 unmodified lines
412418 } => {
413419 const sourceRecord: StoredSourceRecord = {
414420 id: input.source.id,
415− workspaceId: input.source.workspaceId,
421+ scopeId: input.source.scopeId,
416422 catalogId: input.catalogId,
417423 catalogRevisionId: input.catalogRevisionId,
418424 name: input.source.name,
@@ -435,7 +441,7 @@ export const splitSourceForStorage = (input: { · 16 unmodified lines
435441 source: input.source,
436442 auth: input.source.auth,
437443 slot: "runtime",
438− actorAccountId: input.actorAccountId,
444+ actorScopeId: input.actorScopeId,
439445 existingAuthArtifactId: input.existingRuntimeAuthArtifactId
440446 ?? AuthArtifactIdSchema.make(`auth_art_${crypto.randomUUID()}`),
441447 }),
@@ -444,7 +450,7 @@ export const splitSourceForStorage = (input: { · 2 unmodified lines
444450 source: input.source,
445451 auth: input.source.importAuth,
446452 slot: "import",
447− actorAccountId: input.actorAccountId,
453+ actorScopeId: input.actorScopeId,
448454 existingAuthArtifactId: input.existingImportAuthArtifactId
449455 ?? AuthArtifactIdSchema.make(`auth_art_${crypto.randomUUID()}`),
450456 })
@@ -466,7 +472,7 @@ export const projectSourceFromStorage = (input: { · 15 unmodified lines
466472
467473 return {
468474 id: input.sourceRecord.id,
469− workspaceId: input.sourceRecord.workspaceId,
475+ scopeId: input.sourceRecord.scopeId,
470476 name: input.sourceRecord.name,
471477 kind: input.sourceRecord.kind,
472478 endpoint: input.sourceRecord.endpoint,
@@ -507,7 +513,7 @@ export const projectSourcesFromStorage = (input: { · 34 unmodified lines
507513 import: null,
508514 };
509515 const current = authArtifact.slot === "runtime" ? existing.runtime : existing.import;
510− if (current === null || (current.actorAccountId === null && authArtifact.actorAccountId !== null)) {
516+ if (current === null || (current.actorScopeId === null && authArtifact.actorScopeId !== null)) {
511517 authArtifactsBySourceId.set(authArtifact.sourceId, {
512518 ...existing,
513519 [authArtifact.slot]: authArtifact,
packages/platform/sdk/src/runtime/sources/source-discovery.test.ts+8−2
@@ -4,10 +4,16 @@ import { · 3 unmodified lines
44 type ServerResponse,
55 } from "node:http";
66
7−import { describe, expect, it } from "@effect/vitest";
7+import {
8+ describe,
9+ expect,
10+ it,
11+} from "@effect/vitest";
812 import * as Effect from "effect/Effect";
913
10−import { discoverSource } from "./source-discovery";
14+import {
15+ discoverSource,
16+} from "./source-discovery";
1117
1218 type TestServer = {
1319 url: string;
packages/platform/sdk/src/runtime/sources/source-inspection.test.ts+11−3
@@ -1,8 +1,16 @@
1−import { describe, expect, it } from "@effect/vitest";
1+import {
2+ describe,
3+ expect,
4+ it,
5+} from "@effect/vitest";
26 import * as Effect from "effect/Effect";
37
4−import type { LoadedSourceCatalogTool } from "../catalog/source/runtime";
5−import { inspectionToolDetailFromTool } from "./source-inspection";
8+import type {
9+ LoadedSourceCatalogTool,
10+} from "../catalog/source/runtime";
11+import {
12+ inspectionToolDetailFromTool,
13+} from "./source-inspection";
614
715 describe("source inspection", () => {
816 it.effect("keeps the shared TypeScript contract first-class on tool detail", () =>
packages/platform/sdk/src/runtime/sources/source-store.test.ts+20−10
@@ -1,17 +1,27 @@
1−import { describe, expect, it } from "@effect/vitest";
1+import {
2+ describe,
3+ expect,
4+ it,
5+} from "@effect/vitest";
26 import * as Effect from "effect/Effect";
37
48 import {
59 SourceIdSchema,
6− WorkspaceIdSchema,
10+ ScopeIdSchema,
711 type Source,
812 } from "#schema";
913
10−import type { LoadedLocalExecutorConfig } from "../workspace-config";
11−import { buildLocalSourceRecord } from "./source-store";
12−import type { LocalWorkspaceState } from "../workspace-state";
14+import type {
15+ LoadedLocalExecutorConfig,
16+} from "../scope-config";
17+import {
18+ buildLocalSourceRecord,
19+} from "./source-store";
20+import type {
21+ LocalScopeState,
22+} from "../scope-state";
1323
14−const workspaceId = WorkspaceIdSchema.make("ws_source_store");
24+const scopeId = ScopeIdSchema.make("ws_source_store");
1525 const sourceId = SourceIdSchema.make("linear");
1626
1727 const loadedConfig: LoadedLocalExecutorConfig = {
@@ -36,7 +46,7 @@ const loadedConfig: LoadedLocalExecutorConfig = { · 18 unmodified lines
3646 projectConfigPath: "/tmp/project-config.jsonc",
3747 };
3848
39−const workspaceState: LocalWorkspaceState = {
49+const scopeState: LocalScopeState = {
4050 version: 1,
4151 sources: {
4252 [sourceId]: {
@@ -54,16 +64,16 @@ describe("source-store", () => { · 11 unmodified lines
5464 it.effect("projects configured sources without reading local artifacts", () =>
5565 Effect.gen(function* () {
5666 const result = yield* buildLocalSourceRecord({
57− workspaceId,
67+ scopeId,
5868 loadedConfig,
59− workspaceState,
69+ scopeState,
6070 sourceId,
6171 authArtifacts: [],
6272 });
6373
6474 expect(result.source).toEqual({
6575 id: sourceId,
66− workspaceId,
76+ scopeId,
6777 name: "Linear GraphQL",
6878 kind: "graphql",
6979 endpoint: "https://api.linear.app/graphql",
packages/platform/sdk/src/runtime/sources/source-store.ts+60−52
@@ -1,22 +1,30 @@
11 import type {
2− AccountId,
2+ ScopeId,
33 Source,
4− WorkspaceId,
54 } from "#schema";
65 import * as Context from "effect/Context";
76 import * as Effect from "effect/Effect";
87 import * as Layer from "effect/Layer";
98
10−import { SourceTypeDeclarationsRefresherService } from "../catalog/source/type-declarations";
9+import {
10+ SourceTypeDeclarationsRefresherService,
11+} from "../catalog/source/type-declarations";
1112 import {
1213 SourceArtifactStore,
13− type WorkspaceStorageServices,
14− WorkspaceConfigStore,
15− WorkspaceStateStore,
16−} from "../workspace/storage";
17−import { SecretMaterialDeleterService } from "../workspace/secret-material-providers";
18−import { RuntimeLocalWorkspaceService } from "../workspace/runtime-context";
19−import { ExecutorStateStore, type ExecutorStateStoreShape } from "../executor-state-store";
14+ type ScopeStorageServices,
15+ ScopeConfigStore,
16+ ScopeStateStore,
17+} from "../scope/storage";
18+import {
19+ SecretMaterialDeleterService,
20+} from "../scope/secret-material-providers";
21+import {
22+ RuntimeLocalScopeService,
23+} from "../scope/runtime-context";
24+import {
25+ ExecutorStateStore,
26+ type ExecutorStateStoreShape,
27+} from "../executor-state-store";
2028 import {
2129 loadRuntimeSourceStoreDeps,
2230 type RuntimeSourceStoreDeps,
@@ -35,95 +43,95 @@ import { · 12 unmodified lines
3543 export { buildLocalSourceRecord } from "./source-store/records";
3644
3745 type RuntimeSourceStoreShape = {
38− loadSourcesInWorkspace: (
39− workspaceId: WorkspaceId,
40− options?: { actorAccountId?: AccountId | null },
46+ loadSourcesInScope: (
47+ scopeId: ScopeId,
48+ options?: { actorScopeId?: ScopeId | null },
4149 ) => ReturnType<typeof loadSourcesInWorkspaceWithDeps>;
42− listLinkedSecretSourcesInWorkspace: (
43− workspaceId: WorkspaceId,
44− options?: { actorAccountId?: AccountId | null },
50+ listLinkedSecretSourcesInScope: (
51+ scopeId: ScopeId,
52+ options?: { actorScopeId?: ScopeId | null },
4553 ) => ReturnType<typeof listLinkedSecretSourcesInWorkspaceWithDeps>;
4654 loadSourceById: (input: {
47− workspaceId: WorkspaceId;
55+ scopeId: ScopeId;
4856 sourceId: Source["id"];
49− actorAccountId?: AccountId | null;
57+ actorScopeId?: ScopeId | null;
5058 }) => ReturnType<typeof loadSourceByIdWithDeps>;
5159 removeSourceById: (input: {
52− workspaceId: WorkspaceId;
60+ scopeId: ScopeId;
5361 sourceId: Source["id"];
5462 }) => ReturnType<typeof removeSourceByIdWithDeps>;
5563 persistSource: (
5664 source: Source,
57− options?: { actorAccountId?: AccountId | null },
65+ options?: { actorScopeId?: ScopeId | null },
5866 ) => ReturnType<typeof persistSourceWithDeps>;
5967 };
6068
6169 export type RuntimeSourceStore = RuntimeSourceStoreShape;
6270
63−export const loadSourcesInWorkspace = (
71+export const loadSourcesInScope = (
6472 executorState: ExecutorStateStoreShape,
65− workspaceId: WorkspaceId,
73+ scopeId: ScopeId,
6674 options: {
67− actorAccountId?: AccountId | null;
75+ actorScopeId?: ScopeId | null;
6876 } = {},
6977 ): Effect.Effect<
7078 readonly Source[],
7179 Error,
72− WorkspaceStorageServices | SourceTypeDeclarationsRefresherService
80+ ScopeStorageServices | SourceTypeDeclarationsRefresherService
7381 > =>
7482 Effect.flatMap(
75− loadRuntimeSourceStoreDeps(executorState, workspaceId),
76− (deps) => loadSourcesInWorkspaceWithDeps(deps, workspaceId, options),
83+ loadRuntimeSourceStoreDeps(executorState, scopeId),
84+ (deps) => loadSourcesInWorkspaceWithDeps(deps, scopeId, options),
7785 );
7886
79−export const listLinkedSecretSourcesInWorkspace = (
87+export const listLinkedSecretSourcesInScope = (
8088 executorState: ExecutorStateStoreShape,
81− workspaceId: WorkspaceId,
89+ scopeId: ScopeId,
8290 options: {
83− actorAccountId?: AccountId | null;
91+ actorScopeId?: ScopeId | null;
8492 } = {},
8593 ): Effect.Effect<
8694 Map<string, Array<{ sourceId: string; sourceName: string }>>,
8795 Error,
88− WorkspaceStorageServices | SourceTypeDeclarationsRefresherService
96+ ScopeStorageServices | SourceTypeDeclarationsRefresherService
8997 > =>
9098 Effect.flatMap(
91− loadRuntimeSourceStoreDeps(executorState, workspaceId),
92− (deps) => listLinkedSecretSourcesInWorkspaceWithDeps(deps, workspaceId, options),
99+ loadRuntimeSourceStoreDeps(executorState, scopeId),
100+ (deps) => listLinkedSecretSourcesInWorkspaceWithDeps(deps, scopeId, options),
93101 );
94102
95103 export const loadSourceById = (
96104 executorState: ExecutorStateStoreShape,
97105 input: {
98− workspaceId: WorkspaceId;
106+ scopeId: ScopeId;
99107 sourceId: Source["id"];
100− actorAccountId?: AccountId | null;
108+ actorScopeId?: ScopeId | null;
101109 },
102110 ): Effect.Effect<
103111 Source,
104112 Error,
105− WorkspaceStorageServices | SourceTypeDeclarationsRefresherService
113+ ScopeStorageServices | SourceTypeDeclarationsRefresherService
106114 > =>
107115 Effect.flatMap(
108− loadRuntimeSourceStoreDeps(executorState, input.workspaceId),
116+ loadRuntimeSourceStoreDeps(executorState, input.scopeId),
109117 (deps) => loadSourceByIdWithDeps(deps, input),
110118 );
111119
112120 export const removeSourceById = (
113121 executorState: ExecutorStateStoreShape,
114122 input: {
115− workspaceId: WorkspaceId;
123+ scopeId: ScopeId;
116124 sourceId: Source["id"];
117125 },
118126 ): Effect.Effect<
119127 boolean,
120128 Error,
121− | WorkspaceStorageServices
129+ | ScopeStorageServices
122130 | SourceTypeDeclarationsRefresherService
123131 | SecretMaterialDeleterService
124132 > =>
125133 Effect.gen(function* () {
126− const deps = yield* loadRuntimeSourceStoreDeps(executorState, input.workspaceId);
134+ const deps = yield* loadRuntimeSourceStoreDeps(executorState, input.scopeId);
127135 const deleteSecretMaterial = yield* SecretMaterialDeleterService;
128136 return yield* removeSourceByIdWithDeps(deps, input, deleteSecretMaterial);
129137 });
@@ -132,17 +140,17 @@ export const persistSource = ( · 2 unmodified lines
132140 executorState: ExecutorStateStoreShape,
133141 source: Source,
134142 options: {
135− actorAccountId?: AccountId | null;
143+ actorScopeId?: ScopeId | null;
136144 } = {},
137145 ): Effect.Effect<
138146 Source,
139147 Error,
140− | WorkspaceStorageServices
148+ | ScopeStorageServices
141149 | SourceTypeDeclarationsRefresherService
142150 | SecretMaterialDeleterService
143151 > =>
144152 Effect.gen(function* () {
145− const deps = yield* loadRuntimeSourceStoreDeps(executorState, source.workspaceId);
153+ const deps = yield* loadRuntimeSourceStoreDeps(executorState, source.scopeId);
146154 const deleteSecretMaterial = yield* SecretMaterialDeleterService;
147155 return yield* persistSourceWithDeps(deps, source, options, deleteSecretMaterial);
148156 });
@@ -155,9 +163,9 @@ export const RuntimeSourceStoreLive = Layer.effect( · 6 unmodified lines
155163 RuntimeSourceStoreService,
156164 Effect.gen(function* () {
157165 const executorState = yield* ExecutorStateStore;
158− const runtimeLocalWorkspace = yield* RuntimeLocalWorkspaceService;
159− const workspaceConfigStore = yield* WorkspaceConfigStore;
160− const workspaceStateStore = yield* WorkspaceStateStore;
166+ const runtimeLocalScope = yield* RuntimeLocalScopeService;
167+ const scopeConfigStore = yield* ScopeConfigStore;
168+ const scopeStateStore = yield* ScopeStateStore;
161169 const sourceArtifactStore = yield* SourceArtifactStore;
162170 const sourceTypeDeclarationsRefresher =
163171 yield* SourceTypeDeclarationsRefresherService;
@@ -165,18 +173,18 @@ export const RuntimeSourceStoreLive = Layer.effect( · 1 unmodified line
165173
166174 const deps: RuntimeSourceStoreDeps = {
167175 executorState,
168− runtimeLocalWorkspace,
169− workspaceConfigStore,
170− workspaceStateStore,
176+ runtimeLocalScope,
177+ scopeConfigStore,
178+ scopeStateStore,
171179 sourceArtifactStore,
172180 sourceTypeDeclarationsRefresher,
173181 };
174182
175183 return RuntimeSourceStoreService.of({
176− loadSourcesInWorkspace: (workspaceId, options = {}) =>
177− loadSourcesInWorkspaceWithDeps(deps, workspaceId, options),
178− listLinkedSecretSourcesInWorkspace: (workspaceId, options = {}) =>
179− listLinkedSecretSourcesInWorkspaceWithDeps(deps, workspaceId, options),
184+ loadSourcesInScope: (scopeId, options = {}) =>
185+ loadSourcesInWorkspaceWithDeps(deps, scopeId, options),
186+ listLinkedSecretSourcesInScope: (scopeId, options = {}) =>
187+ listLinkedSecretSourcesInWorkspaceWithDeps(deps, scopeId, options),
180188 loadSourceById: (input) =>
181189 loadSourceByIdWithDeps(deps, input),
182190 removeSourceById: (input) =>
packages/platform/sdk/src/runtime/sources/source-store/auth.ts+27−18
@@ -1,18 +1,27 @@
11 import type {
2− AccountId,
2+ ScopeId,
33 AuthArtifact,
44 CredentialSlot,
55 ProviderAuthGrant,
66 Source,
7− WorkspaceId,
87 } from "#schema";
9−import { decodeProviderGrantRefAuthArtifactConfig } from "#schema";
8+import {
9+ decodeProviderGrantRefAuthArtifactConfig,
10+} from "#schema";
1011 import * as Effect from "effect/Effect";
1112
12−import { authArtifactSecretMaterialRefs } from "../../auth/auth-artifacts";
13−import { removeAuthLeaseAndSecrets } from "../../auth/auth-leases";
14−import type { DeleteSecretMaterial } from "../../workspace/secret-material-providers";
15−import type { ExecutorStateStoreShape } from "../../executor-state-store";
13+import {
14+ authArtifactSecretMaterialRefs,
15+} from "../../auth/auth-artifacts";
16+import {
17+ removeAuthLeaseAndSecrets,
18+} from "../../auth/auth-leases";
19+import type {
20+ DeleteSecretMaterial,
21+} from "../../scope/secret-material-providers";
22+import type {
23+ ExecutorStateStoreShape,
24+} from "../../executor-state-store";
1625
1726 const secretRefKey = (ref: { providerId: string; handle: string }): string =>
1827 `${ref.providerId}:${ref.handle}`;
@@ -60,52 +69,52 @@ export const providerGrantIdsFromArtifacts = ( · 41 unmodified lines
6069
6170 export const selectPreferredAuthArtifact = (input: {
6271 authArtifacts: ReadonlyArray<AuthArtifact>;
63− actorAccountId?: AccountId | null;
72+ actorScopeId?: ScopeId | null;
6473 slot: CredentialSlot;
6574 }): AuthArtifact | null => {
6675 const matchingSlot = input.authArtifacts.filter(
6776 (artifact) => artifact.slot === input.slot,
6877 );
6978
70− if (input.actorAccountId !== undefined) {
79+ if (input.actorScopeId !== undefined) {
7180 const exact = matchingSlot.find(
72− (artifact) => artifact.actorAccountId === input.actorAccountId,
81+ (artifact) => artifact.actorScopeId === input.actorScopeId,
7382 );
7483 if (exact) {
7584 return exact;
7685 }
7786 }
7887
79− return matchingSlot.find((artifact) => artifact.actorAccountId === null) ?? null;
88+ return matchingSlot.find((artifact) => artifact.actorScopeId === null) ?? null;
8089 };
8190
8291 export const selectExactAuthArtifact = (input: {
8392 authArtifacts: ReadonlyArray<AuthArtifact>;
84− actorAccountId?: AccountId | null;
93+ actorScopeId?: ScopeId | null;
8594 slot: CredentialSlot;
8695 }): AuthArtifact | null =>
8796 input.authArtifacts.find(
8897 (artifact) =>
8998 artifact.slot === input.slot &&
90− artifact.actorAccountId === (input.actorAccountId ?? null),
99+ artifact.actorScopeId === (input.actorScopeId ?? null),
91100 ) ?? null;
92101
93102 export const removeAuthArtifactsForSource = (
94103 executorState: ExecutorStateStoreShape,
95104 input: {
96− workspaceId: WorkspaceId;
105+ scopeId: ScopeId;
97106 sourceId: Source["id"];
98107 },
99108 deleteSecretMaterial: DeleteSecretMaterial,
100109 ) =>
101110 Effect.gen(function* () {
102− const existingAuthArtifacts = yield* executorState.authArtifacts.listByWorkspaceAndSourceId({
103− workspaceId: input.workspaceId,
111+ const existingAuthArtifacts = yield* executorState.authArtifacts.listByScopeAndSourceId({
112+ scopeId: input.scopeId,
104113 sourceId: input.sourceId,
105114 });
106115
107− yield* executorState.authArtifacts.removeByWorkspaceAndSourceId({
108− workspaceId: input.workspaceId,
116+ yield* executorState.authArtifacts.removeByScopeAndSourceId({
117+ scopeId: input.scopeId,
109118 sourceId: input.sourceId,
110119 });
111120
packages/platform/sdk/src/runtime/sources/source-store/config.ts+16−6
@@ -4,16 +4,26 @@ import type { · 3 unmodified lines
44 Source,
55 SourceId,
66 } from "#schema";
7−import { SourceIdSchema } from "#schema";
7+import {
8+ SourceIdSchema,
9+} from "#schema";
810
9−import type { LoadedLocalExecutorConfig } from "../../workspace-config";
10−import { LocalUnsupportedSourceKindError } from "../../workspace-errors";
11+import type {
12+ LoadedLocalExecutorConfig,
13+} from "../../scope-config";
14+import {
15+ LocalUnsupportedSourceKindError,
16+} from "../../scope-errors";
1117 import {
1218 fromConfigSecretProviderId,
1319 toConfigSecretProviderId,
14−} from "../../workspace/config-secrets";
15−import { getSourceAdapterForSource } from "../source-adapters";
16−import { slugify } from "../slug";
20+} from "../../scope/config-secrets";
21+import {
22+ getSourceAdapterForSource,
23+} from "../source-adapters";
24+import {
25+ slugify,
26+} from "../slug";
1727
1828 export const trimOrNull = (value: string | null | undefined): string | null => {
1929 if (value == null) {
packages/platform/sdk/src/runtime/sources/source-store/deps.ts+60−52
@@ -1,112 +1,120 @@
1−import type { AccountId, WorkspaceId } from "#schema";
1+import type {
2+ ScopeId,
3+} from "#schema";
24 import * as Effect from "effect/Effect";
35
4−import type { LoadedLocalExecutorConfig } from "../../workspace-config";
6+import type {
7+ LoadedLocalExecutorConfig,
8+} from "../../scope-config";
59 import {
610 SourceTypeDeclarationsRefresherService,
711 type SourceTypeDeclarationsRefresherShape,
812 } from "../../catalog/source/type-declarations";
913 import {
10− RuntimeLocalWorkspaceMismatchError,
11− RuntimeLocalWorkspaceUnavailableError,
12−} from "../../workspace-errors";
14+ RuntimeLocalScopeMismatchError,
15+ RuntimeLocalScopeUnavailableError,
16+} from "../../scope-errors";
1317 import {
14− requireRuntimeLocalWorkspace,
15− type RuntimeLocalWorkspaceState,
16−} from "../../workspace/runtime-context";
18+ requireRuntimeLocalScope,
19+ type RuntimeLocalScopeState,
20+} from "../../scope/runtime-context";
1721 import type {
1822 SourceArtifactStoreShape,
19− WorkspaceStorageServices,
20− WorkspaceConfigStoreShape,
21− WorkspaceStateStoreShape,
22−} from "../../workspace/storage";
23+ ScopeStorageServices,
24+ ScopeConfigStoreShape,
25+ ScopeStateStoreShape,
26+} from "../../scope/storage";
2327 import {
2428 SourceArtifactStore,
25− WorkspaceConfigStore,
26− WorkspaceStateStore,
27−} from "../../workspace/storage";
28−import type { LocalWorkspaceState } from "../../workspace-state";
29−import type { ExecutorStateStoreShape } from "../../executor-state-store";
29+ ScopeConfigStore,
30+ ScopeStateStore,
31+} from "../../scope/storage";
32+import type {
33+ LocalScopeState,
34+} from "../../scope-state";
35+import type {
36+ ExecutorStateStoreShape,
37+} from "../../executor-state-store";
3038
3139 export type RuntimeSourceStoreDeps = {
3240 executorState: ExecutorStateStoreShape;
33− runtimeLocalWorkspace: RuntimeLocalWorkspaceState;
34− workspaceConfigStore: WorkspaceConfigStoreShape;
35− workspaceStateStore: WorkspaceStateStoreShape;
41+ runtimeLocalScope: RuntimeLocalScopeState;
42+ scopeConfigStore: ScopeConfigStoreShape;
43+ scopeStateStore: ScopeStateStoreShape;
3644 sourceArtifactStore: SourceArtifactStoreShape;
3745 sourceTypeDeclarationsRefresher: SourceTypeDeclarationsRefresherShape;
3846 };
3947
40−export type ResolvedSourceStoreWorkspace = {
48+export type ResolvedSourceStoreScope = {
4149 installation: {
42− workspaceId: WorkspaceId;
43− accountId: AccountId;
50+ scopeId: ScopeId;
51+ actorScopeId: ScopeId;
4452 };
45− workspaceConfigStore: WorkspaceConfigStoreShape;
46− workspaceStateStore: WorkspaceStateStoreShape;
53+ scopeConfigStore: ScopeConfigStoreShape;
54+ scopeStateStore: ScopeStateStoreShape;
4755 sourceArtifactStore: SourceArtifactStoreShape;
4856 loadedConfig: LoadedLocalExecutorConfig;
49− workspaceState: LocalWorkspaceState;
57+ scopeState: LocalScopeState;
5058 };
5159
5260 export type RuntimeSourceStoreServices =
53− WorkspaceStorageServices | SourceTypeDeclarationsRefresherService;
61+ ScopeStorageServices | SourceTypeDeclarationsRefresherService;
5462
55−export const resolveRuntimeLocalWorkspaceFromDeps = (
63+export const resolveRuntimeLocalScopeFromDeps = (
5664 deps: RuntimeSourceStoreDeps,
57− workspaceId: WorkspaceId,
65+ scopeId: ScopeId,
5866 ): Effect.Effect<
59− ResolvedSourceStoreWorkspace,
60− | RuntimeLocalWorkspaceUnavailableError
61− | RuntimeLocalWorkspaceMismatchError
67+ ResolvedSourceStoreScope,
68+ | RuntimeLocalScopeUnavailableError
69+ | RuntimeLocalScopeMismatchError
6270 | Error,
6371 never
6472 > =>
6573 Effect.gen(function* () {
66− if (deps.runtimeLocalWorkspace.installation.workspaceId !== workspaceId) {
67− return yield* new RuntimeLocalWorkspaceMismatchError({
68− message: `Runtime local workspace mismatch: expected ${workspaceId}, got ${deps.runtimeLocalWorkspace.installation.workspaceId}`,
69− requestedWorkspaceId: workspaceId,
70− activeWorkspaceId: deps.runtimeLocalWorkspace.installation.workspaceId,
74+ if (deps.runtimeLocalScope.installation.scopeId !== scopeId) {
75+ return yield* new RuntimeLocalScopeMismatchError({
76+ message: `Runtime local scope mismatch: expected ${scopeId}, got ${deps.runtimeLocalScope.installation.scopeId}`,
77+ requestedScopeId: scopeId,
78+ activeScopeId: deps.runtimeLocalScope.installation.scopeId,
7179 });
7280 }
7381
74− const loadedConfig = yield* deps.workspaceConfigStore.load();
75− const workspaceState = yield* deps.workspaceStateStore.load();
82+ const loadedConfig = yield* deps.scopeConfigStore.load();
83+ const scopeState = yield* deps.scopeStateStore.load();
7684
7785 return {
78− installation: deps.runtimeLocalWorkspace.installation,
79− workspaceConfigStore: deps.workspaceConfigStore,
80− workspaceStateStore: deps.workspaceStateStore,
86+ installation: deps.runtimeLocalScope.installation,
87+ scopeConfigStore: deps.scopeConfigStore,
88+ scopeStateStore: deps.scopeStateStore,
8189 sourceArtifactStore: deps.sourceArtifactStore,
8290 loadedConfig,
83− workspaceState,
91+ scopeState,
8492 };
8593 });
8694
8795 export const loadRuntimeSourceStoreDeps = (
8896 executorState: ExecutorStateStoreShape,
89− workspaceId: WorkspaceId,
97+ scopeId: ScopeId,
9098 ): Effect.Effect<
9199 RuntimeSourceStoreDeps,
92− | RuntimeLocalWorkspaceUnavailableError
93− | RuntimeLocalWorkspaceMismatchError
100+ | RuntimeLocalScopeUnavailableError
101+ | RuntimeLocalScopeMismatchError
94102 | Error,
95103 RuntimeSourceStoreServices
96104 > =>
97105 Effect.gen(function* () {
98− const runtimeLocalWorkspace = yield* requireRuntimeLocalWorkspace(workspaceId);
99− const workspaceConfigStore = yield* WorkspaceConfigStore;
100− const workspaceStateStore = yield* WorkspaceStateStore;
106+ const runtimeLocalScope = yield* requireRuntimeLocalScope(scopeId);
107+ const scopeConfigStore = yield* ScopeConfigStore;
108+ const scopeStateStore = yield* ScopeStateStore;
101109 const sourceArtifactStore = yield* SourceArtifactStore;
102110 const sourceTypeDeclarationsRefresher =
103111 yield* SourceTypeDeclarationsRefresherService;
104112
105113 return {
106114 executorState,
107− runtimeLocalWorkspace,
108− workspaceConfigStore,
109− workspaceStateStore,
115+ runtimeLocalScope,
116+ scopeConfigStore,
117+ scopeStateStore,
110118 sourceArtifactStore,
111119 sourceTypeDeclarationsRefresher,
112120 };
packages/platform/sdk/src/runtime/sources/source-store/lifecycle.ts+69−60
@@ -1,13 +1,22 @@
1−import type { AccountId, Source, WorkspaceId } from "#schema";
1+import type {
2+ ScopeId,
3+ Source,
4+} from "#schema";
25 import * as Effect from "effect/Effect";
36
4−import { removeAuthLeaseAndSecrets } from "../../auth/auth-leases";
7+import {
8+ removeAuthLeaseAndSecrets,
9+} from "../../auth/auth-leases";
510 import {
611 clearProviderGrantOrphanedAt,
712 markProviderGrantOrphanedIfUnused,
813 } from "../../auth/provider-grant-lifecycle";
9−import type { DeleteSecretMaterial } from "../../workspace/secret-material-providers";
10−import type { LocalWorkspaceState } from "../../workspace-state";
14+import type {
15+ DeleteSecretMaterial,
16+} from "../../scope/secret-material-providers";
17+import type {
18+ LocalScopeState,
19+} from "../../scope-state";
1120 import {
1221 stableSourceCatalogId,
1322 stableSourceCatalogRevisionId,
@@ -26,37 +35,37 @@ import { · 12 unmodified lines
2635 } from "./config";
2736 import {
2837 type RuntimeSourceStoreDeps,
29− resolveRuntimeLocalWorkspaceFromDeps,
38+ resolveRuntimeLocalScopeFromDeps,
3039 } from "./deps";
3140 import {
3241 loadSourceByIdWithDeps,
33− shouldRefreshWorkspaceDeclarationsAfterPersist,
34− syncWorkspaceSourceTypeDeclarationsWithDeps,
42+ shouldRefreshScopeDeclarationsAfterPersist,
43+ syncScopeSourceTypeDeclarationsWithDeps,
3544 } from "./records";
3645
3746 export const removeSourceByIdWithDeps = (
3847 deps: RuntimeSourceStoreDeps,
3948 input: {
40− workspaceId: WorkspaceId;
49+ scopeId: ScopeId;
4150 sourceId: Source["id"];
4251 },
4352 deleteSecretMaterial: DeleteSecretMaterial,
4453 ): Effect.Effect<boolean, Error, never> =>
4554 Effect.gen(function* () {
46− const localWorkspace = yield* resolveRuntimeLocalWorkspaceFromDeps(
55+ const localScope = yield* resolveRuntimeLocalScopeFromDeps(
4756 deps,
48− input.workspaceId,
57+ input.scopeId,
4958 );
50− if (!localWorkspace.loadedConfig.config?.sources?.[input.sourceId]) {
59+ if (!localScope.loadedConfig.config?.sources?.[input.sourceId]) {
5160 return false;
5261 }
5362
54− const projectConfig = cloneJson(localWorkspace.loadedConfig.projectConfig ?? {});
63+ const projectConfig = cloneJson(localScope.loadedConfig.projectConfig ?? {});
5564 const sources = {
5665 ...projectConfig.sources,
5766 };
5867 delete sources[input.sourceId];
59− yield* localWorkspace.workspaceConfigStore.writeProject({
68+ yield* localScope.scopeConfigStore.writeProject({
6069 config: {
6170 ...projectConfig,
6271 sources,
@@ -64,30 +73,30 @@ export const removeSourceByIdWithDeps = ( · 1 unmodified line
6473 });
6574
6675 const { [input.sourceId]: _removedSource, ...remainingSources } =
67− localWorkspace.workspaceState.sources;
68− const workspaceState: LocalWorkspaceState = {
69− ...localWorkspace.workspaceState,
76+ localScope.scopeState.sources;
77+ const scopeState: LocalScopeState = {
78+ ...localScope.scopeState,
7079 sources: remainingSources,
7180 };
72− yield* localWorkspace.workspaceStateStore.write({
73− state: workspaceState,
81+ yield* localScope.scopeStateStore.write({
82+ state: scopeState,
7483 });
75− yield* localWorkspace.sourceArtifactStore.remove({
84+ yield* localScope.sourceArtifactStore.remove({
7685 sourceId: input.sourceId,
7786 });
7887 const existingAuthArtifacts =
79− yield* deps.executorState.authArtifacts.listByWorkspaceAndSourceId({
80− workspaceId: input.workspaceId,
88+ yield* deps.executorState.authArtifacts.listByScopeAndSourceId({
89+ scopeId: input.scopeId,
8190 sourceId: input.sourceId,
8291 });
8392 const removedGrantIds = providerGrantIdsFromArtifacts(existingAuthArtifacts);
8493
85− yield* deps.executorState.sourceAuthSessions.removeByWorkspaceAndSourceId(
86− input.workspaceId,
94+ yield* deps.executorState.sourceAuthSessions.removeByScopeAndSourceId(
95+ input.scopeId,
8796 input.sourceId,
8897 );
89− yield* deps.executorState.sourceOauthClients.removeByWorkspaceAndSourceId({
90− workspaceId: input.workspaceId,
98+ yield* deps.executorState.sourceOauthClients.removeByScopeAndSourceId({
99+ scopeId: input.scopeId,
91100 sourceId: input.sourceId,
92101 });
93102 yield* removeAuthArtifactsForSource(deps.executorState, input, deleteSecretMaterial);
@@ -95,12 +104,12 @@ export const removeSourceByIdWithDeps = ( · 1 unmodified line
95104 [...removedGrantIds],
96105 (grantId) =>
97106 markProviderGrantOrphanedIfUnused(deps.executorState, {
98− workspaceId: input.workspaceId,
107+ scopeId: input.scopeId,
99108 grantId,
100109 }),
101110 { discard: true },
102111 );
103− yield* syncWorkspaceSourceTypeDeclarationsWithDeps(deps, input.workspaceId);
112+ yield* syncScopeSourceTypeDeclarationsWithDeps(deps, input.scopeId);
104113
105114 return true;
106115 });
@@ -109,42 +118,42 @@ export const persistSourceWithDeps = ( · 2 unmodified lines
109118 deps: RuntimeSourceStoreDeps,
110119 source: Source,
111120 options: {
112− actorAccountId?: AccountId | null;
121+ actorScopeId?: ScopeId | null;
113122 } = {},
114123 deleteSecretMaterial: DeleteSecretMaterial,
115124 ): Effect.Effect<Source, Error, never> =>
116125 Effect.gen(function* () {
117− const localWorkspace = yield* resolveRuntimeLocalWorkspaceFromDeps(
126+ const localScope = yield* resolveRuntimeLocalScopeFromDeps(
118127 deps,
119− source.workspaceId,
128+ source.scopeId,
120129 );
121130 const nextSource = {
122131 ...source,
123132 id:
124− localWorkspace.loadedConfig.config?.sources?.[source.id] ||
125− localWorkspace.workspaceState.sources[source.id]
133+ localScope.loadedConfig.config?.sources?.[source.id] ||
134+ localScope.scopeState.sources[source.id]
126135 ? source.id
127136 : deriveLocalSourceId(
128137 source,
129− new Set(Object.keys(localWorkspace.loadedConfig.config?.sources ?? {})),
138+ new Set(Object.keys(localScope.loadedConfig.config?.sources ?? {})),
130139 ),
131140 } satisfies Source;
132141 const existingAuthArtifacts =
133− yield* deps.executorState.authArtifacts.listByWorkspaceAndSourceId({
134− workspaceId: nextSource.workspaceId,
142+ yield* deps.executorState.authArtifacts.listByScopeAndSourceId({
143+ scopeId: nextSource.scopeId,
135144 sourceId: nextSource.id,
136145 });
137146 const existingRuntimeAuthArtifact = selectExactAuthArtifact({
138147 authArtifacts: existingAuthArtifacts,
139− actorAccountId: options.actorAccountId,
148+ actorScopeId: options.actorScopeId,
140149 slot: "runtime",
141150 });
142151 const existingImportAuthArtifact = selectExactAuthArtifact({
143152 authArtifacts: existingAuthArtifacts,
144− actorAccountId: options.actorAccountId,
153+ actorScopeId: options.actorScopeId,
145154 slot: "import",
146155 });
147− const projectConfig = cloneJson(localWorkspace.loadedConfig.projectConfig ?? {});
156+ const projectConfig = cloneJson(localScope.loadedConfig.projectConfig ?? {});
148157 const sources = {
149158 ...projectConfig.sources,
150159 };
@@ -152,9 +161,9 @@ export const persistSourceWithDeps = ( · 1 unmodified line
152161 sources[nextSource.id] = configSourceFromLocalSource({
153162 source: nextSource,
154163 existingConfigAuth: existingConfigSource?.connection.auth,
155− config: localWorkspace.loadedConfig.config,
164+ config: localScope.loadedConfig.config,
156165 });
157− yield* localWorkspace.workspaceConfigStore.writeProject({
166+ yield* localScope.scopeConfigStore.writeProject({
158167 config: {
159168 ...projectConfig,
160169 sources,
@@ -165,7 +174,7 @@ export const persistSourceWithDeps = ( · 4 unmodified lines
165174 source: nextSource,
166175 catalogId: stableSourceCatalogId(nextSource),
167176 catalogRevisionId: stableSourceCatalogRevisionId(nextSource),
168− actorAccountId: options.actorAccountId,
177+ actorScopeId: options.actorScopeId,
169178 existingRuntimeAuthArtifactId: existingRuntimeAuthArtifact?.id ?? null,
170179 existingImportAuthArtifactId: existingImportAuthArtifact?.id ?? null,
171180 });
@@ -176,10 +185,10 @@ export const persistSourceWithDeps = ( · 4 unmodified lines
176185 authArtifactId: existingRuntimeAuthArtifact.id,
177186 }, deleteSecretMaterial);
178187 }
179− yield* deps.executorState.authArtifacts.removeByWorkspaceSourceAndActor({
180− workspaceId: nextSource.workspaceId,
188+ yield* deps.executorState.authArtifacts.removeByScopeSourceAndActor({
189+ scopeId: nextSource.scopeId,
181190 sourceId: nextSource.id,
182− actorAccountId: options.actorAccountId ?? null,
191+ actorScopeId: options.actorScopeId ?? null,
183192 slot: "runtime",
184193 });
185194 } else {
@@ -205,10 +214,10 @@ export const persistSourceWithDeps = ( · 19 unmodified lines
205214 authArtifactId: existingImportAuthArtifact.id,
206215 }, deleteSecretMaterial);
207216 }
208− yield* deps.executorState.authArtifacts.removeByWorkspaceSourceAndActor({
209− workspaceId: nextSource.workspaceId,
217+ yield* deps.executorState.authArtifacts.removeByScopeSourceAndActor({
218+ scopeId: nextSource.scopeId,
210219 sourceId: nextSource.id,
211− actorAccountId: options.actorAccountId ?? null,
220+ actorScopeId: options.actorScopeId ?? null,
212221 slot: "import",
213222 });
214223 } else {
@@ -249,17 +258,17 @@ export const persistSourceWithDeps = ( · 34 unmodified lines
249258 [...previousGrantIds].filter((grantId) => !nextGrantIds.has(grantId)),
250259 (grantId) =>
251260 markProviderGrantOrphanedIfUnused(deps.executorState, {
252− workspaceId: nextSource.workspaceId,
261+ scopeId: nextSource.scopeId,
253262 grantId,
254263 }),
255264 { discard: true },
256265 );
257266
258− const existingSourceState = localWorkspace.workspaceState.sources[nextSource.id];
259− const workspaceState: LocalWorkspaceState = {
260− ...localWorkspace.workspaceState,
267+ const existingSourceState = localScope.scopeState.sources[nextSource.id];
268+ const scopeState: LocalScopeState = {
269+ ...localScope.scopeState,
261270 sources: {
262− ...localWorkspace.workspaceState.sources,
271+ ...localScope.scopeState.sources,
263272 [nextSource.id]: {
264273 status: nextSource.status,
265274 lastError: nextSource.lastError,
@@ -269,27 +278,27 @@ export const persistSourceWithDeps = ( · 3 unmodified lines
269278 },
270279 },
271280 };
272− yield* localWorkspace.workspaceStateStore.write({
273− state: workspaceState,
281+ yield* localScope.scopeStateStore.write({
282+ state: scopeState,
274283 });
275284
276− if (shouldRefreshWorkspaceDeclarationsAfterPersist(nextSource)) {
277− yield* syncWorkspaceSourceTypeDeclarationsWithDeps(
285+ if (shouldRefreshScopeDeclarationsAfterPersist(nextSource)) {
286+ yield* syncScopeSourceTypeDeclarationsWithDeps(
278287 deps,
279− nextSource.workspaceId,
288+ nextSource.scopeId,
280289 options,
281290 );
282291 }
283292
284293 return yield* loadSourceByIdWithDeps(deps, {
285− workspaceId: nextSource.workspaceId,
294+ scopeId: nextSource.scopeId,
286295 sourceId: nextSource.id,
287− actorAccountId: options.actorAccountId,
296+ actorScopeId: options.actorScopeId,
288297 });
289298 }).pipe(
290299 Effect.withSpan("source.store.persist", {
291300 attributes: {
292− "executor.workspace.id": source.workspaceId,
301+ "executor.scope.id": source.scopeId,
293302 "executor.source.id": source.id,
294303 "executor.source.kind": source.kind,
295304 "executor.source.status": source.status,
packages/platform/sdk/src/runtime/sources/source-store/records.ts+76−65
@@ -1,25 +1,36 @@
11 import type {
2− AccountId,
2+ ScopeId,
33 AuthArtifact,
44 Source,
55 SourceId,
6− WorkspaceId,
76 } from "#schema";
8−import { SourceIdSchema } from "#schema";
7+import {
8+ SourceIdSchema,
9+} from "#schema";
910 import * as Effect from "effect/Effect";
1011
11−import { sourceAuthFromAuthArtifact } from "../../auth/auth-artifacts";
12−import { authArtifactSecretMaterialRefs } from "../../auth/auth-artifacts";
13−import type { LoadedLocalExecutorConfig } from "../../workspace-config";
12+import {
13+ sourceAuthFromAuthArtifact,
14+} from "../../auth/auth-artifacts";
15+import {
16+ authArtifactSecretMaterialRefs,
17+} from "../../auth/auth-artifacts";
18+import type {
19+ LoadedLocalExecutorConfig,
20+} from "../../scope-config";
1421 import {
1522 LocalConfiguredSourceNotFoundError,
16− RuntimeLocalWorkspaceMismatchError,
17− RuntimeLocalWorkspaceUnavailableError,
18−} from "../../workspace-errors";
19−import type { LocalWorkspaceState } from "../../workspace-state";
20−import { getSourceAdapter } from "../source-adapters";
23+ RuntimeLocalScopeMismatchError,
24+ RuntimeLocalScopeUnavailableError,
25+} from "../../scope-errors";
26+import type {
27+ LocalScopeState,
28+} from "../../scope-state";
29+import {
30+ getSourceAdapter,
31+} from "../source-adapters";
2132 import {
22− resolveRuntimeLocalWorkspaceFromDeps,
33+ resolveRuntimeLocalScopeFromDeps,
2334 type RuntimeSourceStoreDeps,
2435 } from "./deps";
2536 import {
@@ -31,11 +42,11 @@ import { · 5 unmodified lines
3142 } from "./config";
3243
3344 export const buildLocalSourceRecord = (input: {
34− workspaceId: WorkspaceId;
45+ scopeId: ScopeId;
3546 loadedConfig: LoadedLocalExecutorConfig;
36− workspaceState: LocalWorkspaceState;
47+ scopeState: LocalScopeState;
3748 sourceId: SourceId;
38− actorAccountId?: AccountId | null;
49+ actorScopeId?: ScopeId | null;
3950 authArtifacts: ReadonlyArray<AuthArtifact>;
4051 }): Effect.Effect<
4152 {
@@ -54,11 +65,11 @@ export const buildLocalSourceRecord = (input: { · 12 unmodified lines
5465 });
5566 }
5667
57− const existingState = input.workspaceState.sources[input.sourceId];
68+ const existingState = input.scopeState.sources[input.sourceId];
5869 const adapter = getSourceAdapter(sourceConfig.kind);
5970 const baseSource = (yield* adapter.validateSource({
6071 id: SourceIdSchema.make(input.sourceId),
61− workspaceId: input.workspaceId,
72+ scopeId: input.scopeId,
6273 name: trimOrNull(sourceConfig.name) ?? input.sourceId,
6374 kind: sourceConfig.kind,
6475 endpoint: sourceConfig.connection.endpoint.trim(),
@@ -86,14 +97,14 @@ export const buildLocalSourceRecord = (input: { · 21 unmodified lines
8697 authArtifacts: input.authArtifacts.filter(
8798 (artifactItem) => artifactItem.sourceId === baseSource.id,
8899 ),
89− actorAccountId: input.actorAccountId,
100+ actorScopeId: input.actorScopeId,
90101 slot: "runtime",
91102 });
92103 const importAuthArtifact = selectPreferredAuthArtifact({
93104 authArtifacts: input.authArtifacts.filter(
94105 (artifactItem) => artifactItem.sourceId === baseSource.id,
95106 ),
96− actorAccountId: input.actorAccountId,
107+ actorScopeId: input.actorScopeId,
97108 slot: "import",
98109 });
99110
@@ -119,36 +130,36 @@ export const buildLocalSourceRecord = (input: { · 19 unmodified lines
119130
120131 export const loadSourcesInWorkspaceWithDeps = (
121132 deps: RuntimeSourceStoreDeps,
122− workspaceId: WorkspaceId,
133+ scopeId: ScopeId,
123134 options: {
124− actorAccountId?: AccountId | null;
135+ actorScopeId?: ScopeId | null;
125136 } = {},
126137 ): Effect.Effect<
127138 readonly Source[],
128− | RuntimeLocalWorkspaceUnavailableError
129− | RuntimeLocalWorkspaceMismatchError
139+ | RuntimeLocalScopeUnavailableError
140+ | RuntimeLocalScopeMismatchError
130141 | LocalConfiguredSourceNotFoundError
131142 | Error,
132143 never
133144 > =>
134145 Effect.gen(function* () {
135− const localWorkspace = yield* resolveRuntimeLocalWorkspaceFromDeps(
146+ const localScope = yield* resolveRuntimeLocalScopeFromDeps(
136147 deps,
137− workspaceId,
148+ scopeId,
138149 );
139− const authArtifacts = yield* deps.executorState.authArtifacts.listByWorkspaceId(
140− workspaceId,
150+ const authArtifacts = yield* deps.executorState.authArtifacts.listByScopeId(
151+ scopeId,
141152 );
142153 const sources = yield* Effect.forEach(
143− Object.keys(localWorkspace.loadedConfig.config?.sources ?? {}),
154+ Object.keys(localScope.loadedConfig.config?.sources ?? {}),
144155 (sourceId) =>
145156 Effect.map(
146157 buildLocalSourceRecord({
147− workspaceId,
148− loadedConfig: localWorkspace.loadedConfig,
149− workspaceState: localWorkspace.workspaceState,
158+ scopeId,
159+ loadedConfig: localScope.loadedConfig,
160+ scopeState: localScope.scopeState,
150161 sourceId: SourceIdSchema.make(sourceId),
151− actorAccountId: options.actorAccountId,
162+ actorScopeId: options.actorScopeId,
152163 authArtifacts,
153164 }),
154165 ({ source }) => source,
@@ -157,28 +168,28 @@ export const loadSourcesInWorkspaceWithDeps = ( · 2 unmodified lines
157168 yield* Effect.annotateCurrentSpan("executor.source.count", sources.length);
158169 return sources;
159170 }).pipe(
160− Effect.withSpan("source.store.load_workspace", {
171+ Effect.withSpan("source.store.load_scope", {
161172 attributes: {
162− "executor.workspace.id": workspaceId,
173+ "executor.scope.id": scopeId,
163174 },
164175 }),
165176 );
166177
167−export const syncWorkspaceSourceTypeDeclarationsWithDeps = (
178+export const syncScopeSourceTypeDeclarationsWithDeps = (
168179 deps: RuntimeSourceStoreDeps,
169− workspaceId: WorkspaceId,
180+ scopeId: ScopeId,
170181 options: {
171− actorAccountId?: AccountId | null;
182+ actorScopeId?: ScopeId | null;
172183 } = {},
173184 ): Effect.Effect<void, Error, never> =>
174185 Effect.gen(function* () {
175− const localWorkspace = yield* resolveRuntimeLocalWorkspaceFromDeps(
186+ const localScope = yield* resolveRuntimeLocalScopeFromDeps(
176187 deps,
177− workspaceId,
188+ scopeId,
178189 );
179190 const sources = yield* loadSourcesInWorkspaceWithDeps(
180191 deps,
181− workspaceId,
192+ scopeId,
182193 options,
183194 );
184195 const entries = yield* Effect.forEach(sources, (source) =>
@@ -202,14 +213,14 @@ export const syncWorkspaceSourceTypeDeclarationsWithDeps = ( · 17 unmodified lines
202213 ),
203214 });
204215 }).pipe(
205− Effect.withSpan("source.types.refresh_workspace.schedule", {
216+ Effect.withSpan("source.types.refresh_scope.schedule", {
206217 attributes: {
207− "executor.workspace.id": workspaceId,
218+ "executor.scope.id": scopeId,
208219 },
209220 }),
210221 );
211222
212−export const shouldRefreshWorkspaceDeclarationsAfterPersist = (source: Source): boolean =>
223+export const shouldRefreshScopeDeclarationsAfterPersist = (source: Source): boolean =>
213224 source.enabled === false ||
214225 source.status === "auth_required" ||
215226 source.status === "error" ||
@@ -217,24 +228,24 @@ export const shouldRefreshWorkspaceDeclarationsAfterPersist = (source: Source): · 1 unmodified line
217228
218229 export const listLinkedSecretSourcesInWorkspaceWithDeps = (
219230 deps: RuntimeSourceStoreDeps,
220− workspaceId: WorkspaceId,
231+ scopeId: ScopeId,
221232 options: {
222− actorAccountId?: AccountId | null;
233+ actorScopeId?: ScopeId | null;
223234 } = {},
224235 ): Effect.Effect<
225236 Map<string, Array<{ sourceId: string; sourceName: string }>>,
226− | RuntimeLocalWorkspaceUnavailableError
227− | RuntimeLocalWorkspaceMismatchError
237+ | RuntimeLocalScopeUnavailableError
238+ | RuntimeLocalScopeMismatchError
228239 | LocalConfiguredSourceNotFoundError
229240 | Error,
230241 never
231242 > =>
232243 Effect.gen(function* () {
233244 const [sources, authArtifacts, materialIds] = yield* Effect.all([
234− loadSourcesInWorkspaceWithDeps(deps, workspaceId, {
235− actorAccountId: options.actorAccountId,
245+ loadSourcesInWorkspaceWithDeps(deps, scopeId, {
246+ actorScopeId: options.actorScopeId,
236247 }),
237− deps.executorState.authArtifacts.listByWorkspaceId(workspaceId),
248+ deps.executorState.authArtifacts.listByScopeId(scopeId),
238249 deps.executorState.secretMaterials.listAll().pipe(
239250 Effect.map(
240251 (materials) => new Set(materials.map((material) => String(material.id))),
@@ -273,39 +284,39 @@ export const listLinkedSecretSourcesInWorkspaceWithDeps = ( · 32 unmodified lines
273284 export const loadSourceByIdWithDeps = (
274285 deps: RuntimeSourceStoreDeps,
275286 input: {
276− workspaceId: WorkspaceId;
287+ scopeId: ScopeId;
277288 sourceId: Source["id"];
278− actorAccountId?: AccountId | null;
289+ actorScopeId?: ScopeId | null;
279290 },
280291 ): Effect.Effect<
281292 Source,
282− | RuntimeLocalWorkspaceUnavailableError
283− | RuntimeLocalWorkspaceMismatchError
293+ | RuntimeLocalScopeUnavailableError
294+ | RuntimeLocalScopeMismatchError
284295 | LocalConfiguredSourceNotFoundError
285296 | Error,
286297 never
287298 > =>
288299 Effect.gen(function* () {
289− const localWorkspace = yield* resolveRuntimeLocalWorkspaceFromDeps(
300+ const localScope = yield* resolveRuntimeLocalScopeFromDeps(
290301 deps,
291− input.workspaceId,
302+ input.scopeId,
292303 );
293− const authArtifacts = yield* deps.executorState.authArtifacts.listByWorkspaceId(
294− input.workspaceId,
304+ const authArtifacts = yield* deps.executorState.authArtifacts.listByScopeId(
305+ input.scopeId,
295306 );
296− if (!localWorkspace.loadedConfig.config?.sources?.[input.sourceId]) {
307+ if (!localScope.loadedConfig.config?.sources?.[input.sourceId]) {
297308 return yield* new LocalConfiguredSourceNotFoundError({
298− message: `Source not found: workspaceId=${input.workspaceId} sourceId=${input.sourceId}`,
309+ message: `Source not found: scopeId=${input.scopeId} sourceId=${input.sourceId}`,
299310 sourceId: input.sourceId,
300311 });
301312 }
302313
303314 const localSource = yield* buildLocalSourceRecord({
304− workspaceId: input.workspaceId,
305− loadedConfig: localWorkspace.loadedConfig,
306− workspaceState: localWorkspace.workspaceState,
315+ scopeId: input.scopeId,
316+ loadedConfig: localScope.loadedConfig,
317+ scopeState: localScope.scopeState,
307318 sourceId: input.sourceId,
308− actorAccountId: input.actorAccountId,
319+ actorScopeId: input.actorScopeId,
309320 authArtifacts,
310321 });
311322
@@ -313,7 +324,7 @@ export const loadSourceByIdWithDeps = ( · 1 unmodified line
313324 }).pipe(
314325 Effect.withSpan("source.store.load_by_id", {
315326 attributes: {
316− "executor.workspace.id": input.workspaceId,
327+ "executor.scope.id": input.scopeId,
317328 "executor.source.id": input.sourceId,
318329 },
319330 }),
packages/platform/sdk/src/runtime/workspace/runtime-context.ts+0−78
@@ -1,78 +0,0 @@
1−import * as Context from "effect/Context";
2−import * as Effect from "effect/Effect";
3−import * as Layer from "effect/Layer";
4−import * as Option from "effect/Option";
5−
6−import type { AccountId, WorkspaceId } from "#schema";
7−import type { ExecutorWorkspaceContext } from "../../workspace";
8−import type { LoadedLocalExecutorConfig } from "../workspace-config";
9−import {
10− RuntimeLocalWorkspaceMismatchError,
11− RuntimeLocalWorkspaceUnavailableError,
12−} from "../workspace-errors";
13−
14−export type RuntimeLocalWorkspaceState = {
15− workspace: ExecutorWorkspaceContext;
16− installation: {
17− workspaceId: WorkspaceId;
18− accountId: AccountId;
19− };
20− loadedConfig: LoadedLocalExecutorConfig;
21−};
22−
23−export class RuntimeLocalWorkspaceService extends Context.Tag(
24− "#runtime/RuntimeLocalWorkspaceService",
25−)<RuntimeLocalWorkspaceService, RuntimeLocalWorkspaceState>() {}
26−
27−export const RuntimeLocalWorkspaceLive = (
28− runtimeLocalWorkspace: RuntimeLocalWorkspaceState,
29−) => Layer.succeed(RuntimeLocalWorkspaceService, runtimeLocalWorkspace);
30−
31−export const provideOptionalRuntimeLocalWorkspace = <A, E, R>(
32− effect: Effect.Effect<A, E, R>,
33− runtimeLocalWorkspace: RuntimeLocalWorkspaceState | null | undefined,
34−): Effect.Effect<A, E, R> =>
35− runtimeLocalWorkspace === null || runtimeLocalWorkspace === undefined
36− ? effect
37− : effect.pipe(Effect.provide(RuntimeLocalWorkspaceLive(runtimeLocalWorkspace)));
38−
39−export const getRuntimeLocalWorkspaceOption = () =>
40− Effect.contextWith((context) =>
41− Context.getOption(context, RuntimeLocalWorkspaceService),
42− ).pipe(
43− Effect.map((option) => (Option.isSome(option) ? option.value : null)),
44− ) as Effect.Effect<RuntimeLocalWorkspaceState | null, never, never>;
45−
46−export const requireRuntimeLocalWorkspace = (
47− workspaceId?: WorkspaceId,
48−): Effect.Effect<
49− RuntimeLocalWorkspaceState,
50− RuntimeLocalWorkspaceUnavailableError | RuntimeLocalWorkspaceMismatchError,
51− never
52−> =>
53− Effect.gen(function* () {
54− const runtimeLocalWorkspace = yield* getRuntimeLocalWorkspaceOption();
55− if (runtimeLocalWorkspace === null) {
56− return yield* new RuntimeLocalWorkspaceUnavailableError({
57− message: "Runtime local workspace is unavailable",
58− });
59− }
60−
61− if (
62− workspaceId !== undefined
63− && runtimeLocalWorkspace.installation.workspaceId !== workspaceId
64− ) {
65− return yield* new RuntimeLocalWorkspaceMismatchError({
66− message: `Workspace ${workspaceId} is not the active local workspace ${runtimeLocalWorkspace.installation.workspaceId}`,
67− requestedWorkspaceId: workspaceId,
68− activeWorkspaceId: runtimeLocalWorkspace.installation.workspaceId,
69− });
70− }
71−
72− return runtimeLocalWorkspace;
73− });
74−
75−export const requireRuntimeLocalAccountId = (workspaceId?: WorkspaceId) =>
76− requireRuntimeLocalWorkspace(workspaceId).pipe(
77− Effect.map((runtimeLocalWorkspace) => runtimeLocalWorkspace.installation.accountId),
78− );
packages/platform/sdk/src/schema/common.ts+3−1
@@ -1,4 +1,6 @@
1−import { Schema } from "effect";
1+import {
2+ Schema,
3+} from "effect";
24
35 export const TimestampMsSchema = Schema.Number;
46
packages/platform/sdk/src/schema/enums.ts+4−4
@@ -54,8 +54,8 @@ export { · 53 unmodified lines
5454 type SourceAuthSessionStatus,
5555 } from "./models/source-auth-session";
5656 export {
57− LocalWorkspacePolicyApprovalModeSchema,
58− LocalWorkspacePolicyEffectSchema,
59− type LocalWorkspacePolicyApprovalMode,
60− type LocalWorkspacePolicyEffect,
57+ LocalScopePolicyApprovalModeSchema,
58+ LocalScopePolicyEffectSchema,
59+ type LocalScopePolicyApprovalMode,
60+ type LocalScopePolicyEffect,
6161 } from "./models/policy";
packages/platform/sdk/src/schema/ids.ts+11−11
@@ -1,7 +1,8 @@
1−import { Schema } from "effect";
1+import {
2+ Schema,
3+} from "effect";
24
3−export const AccountIdSchema = Schema.String.pipe(Schema.brand("AccountId"));
4−export const WorkspaceIdSchema = Schema.String.pipe(Schema.brand("WorkspaceId"));
5+export const ScopeIdSchema = Schema.String.pipe(Schema.brand("ScopeId"));
56 export const SourceIdSchema = Schema.String.pipe(Schema.brand("SourceId"));
67 export const SourceCatalogIdSchema = Schema.String.pipe(Schema.brand("SourceCatalogId"));
78 export const SourceCatalogRevisionIdSchema = Schema.String.pipe(
@@ -17,11 +18,11 @@ export const AuthLeaseIdSchema = Schema.String.pipe( · 9 unmodified lines
1718 Schema.brand("AuthLeaseId"),
1819 );
1920 export const CredentialIdSchema = AuthArtifactIdSchema;
20−export const WorkspaceSourceOauthClientIdSchema = Schema.String.pipe(
21− Schema.brand("WorkspaceSourceOauthClientId"),
21+export const ScopedSourceOauthClientIdSchema = Schema.String.pipe(
22+ Schema.brand("ScopedSourceOauthClientId"),
2223 );
23−export const WorkspaceOauthClientIdSchema = Schema.String.pipe(
24− Schema.brand("WorkspaceOauthClientId"),
24+export const ScopeOauthClientIdSchema = Schema.String.pipe(
25+ Schema.brand("ScopeOauthClientId"),
2526 );
2627 export const ProviderAuthGrantIdSchema = Schema.String.pipe(
2728 Schema.brand("ProviderAuthGrantId"),
@@ -38,8 +39,7 @@ export const ExecutionStepIdSchema = Schema.String.pipe( · 10 unmodified lines
3839 Schema.brand("ExecutionStepId"),
3940 );
4041
41−export type AccountId = typeof AccountIdSchema.Type;
42−export type WorkspaceId = typeof WorkspaceIdSchema.Type;
42+export type ScopeId = typeof ScopeIdSchema.Type;
4343 export type SourceId = typeof SourceIdSchema.Type;
4444 export type SourceCatalogId = typeof SourceCatalogIdSchema.Type;
4545 export type SourceCatalogRevisionId = typeof SourceCatalogRevisionIdSchema.Type;
@@ -47,8 +47,8 @@ export type SourceAuthSessionId = typeof SourceAuthSessionIdSchema.Type; · 1 unmodified line
4747 export type AuthArtifactId = typeof AuthArtifactIdSchema.Type;
4848 export type AuthLeaseId = typeof AuthLeaseIdSchema.Type;
4949 export type CredentialId = typeof CredentialIdSchema.Type;
50−export type WorkspaceSourceOauthClientId = typeof WorkspaceSourceOauthClientIdSchema.Type;
51−export type WorkspaceOauthClientId = typeof WorkspaceOauthClientIdSchema.Type;
50+export type ScopedSourceOauthClientId = typeof ScopedSourceOauthClientIdSchema.Type;
51+export type ScopeOauthClientId = typeof ScopeOauthClientIdSchema.Type;
5252 export type ProviderAuthGrantId = typeof ProviderAuthGrantIdSchema.Type;
5353 export type SecretMaterialId = typeof SecretMaterialIdSchema.Type;
5454 export type PolicyId = typeof PolicyIdSchema.Type;
packages/platform/sdk/src/schema/index.ts+1−1
@@ -7,7 +7,7 @@ export * from "./models/source-catalog"; · 6 unmodified lines
77 export * from "./models/auth-artifact";
88 export * from "./models/auth-lease";
99 export * from "./models/source-oauth-client";
10−export * from "./models/workspace-oauth-client";
10+export * from "./models/scope-oauth-client";
1111 export * from "./models/provider-auth-grant";
1212 export * from "./models/secret-material";
1313 export * from "./models/source-auth-session";
packages/platform/sdk/src/schema/models/auth-artifact.ts+9−6
@@ -1,13 +1,16 @@
1−import { Schema } from "effect";
1+import {
2+ Schema,
3+} from "effect";
24 import * as Option from "effect/Option";
35
4−import { TimestampMsSchema } from "../common";
56 import {
6− AccountIdSchema,
7+ TimestampMsSchema,
8+} from "../common";
9+import {
10+ ScopeIdSchema,
711 AuthArtifactIdSchema,
812 ProviderAuthGrantIdSchema,
913 SourceIdSchema,
10− WorkspaceIdSchema,
1114 } from "../ids";
1215
1316 export const SecretRefSchema = Schema.Struct({
@@ -207,9 +210,9 @@ const decodeAuthGrantSetOption = Schema.decodeUnknownOption(AuthGrantSetJsonSche · 193 unmodified lines
207210
208211 export const AuthArtifactSchema = Schema.Struct({
209212 id: AuthArtifactIdSchema,
210− workspaceId: WorkspaceIdSchema,
213+ scopeId: ScopeIdSchema,
211214 sourceId: SourceIdSchema,
212− actorAccountId: Schema.NullOr(AccountIdSchema),
215+ actorScopeId: Schema.NullOr(ScopeIdSchema),
213216 slot: AuthArtifactSlotSchema,
214217 artifactKind: AuthArtifactKindSchema,
215218 configJson: Schema.String,
packages/platform/sdk/src/schema/models/auth-lease.ts+9−6
@@ -1,13 +1,16 @@
1−import { Schema } from "effect";
1+import {
2+ Schema,
3+} from "effect";
24 import * as Option from "effect/Option";
35
4−import { TimestampMsSchema } from "../common";
56 import {
6− AccountIdSchema,
7+ TimestampMsSchema,
8+} from "../common";
9+import {
10+ ScopeIdSchema,
711 AuthArtifactIdSchema,
812 AuthLeaseIdSchema,
913 SourceIdSchema,
10− WorkspaceIdSchema,
1114 } from "../ids";
1215
1316 import {
@@ -19,9 +22,9 @@ import { · 5 unmodified lines
1922 export const AuthLeaseSchema = Schema.Struct({
2023 id: AuthLeaseIdSchema,
2124 authArtifactId: AuthArtifactIdSchema,
22− workspaceId: WorkspaceIdSchema,
25+ scopeId: ScopeIdSchema,
2326 sourceId: SourceIdSchema,
24− actorAccountId: Schema.NullOr(AccountIdSchema),
27+ actorScopeId: Schema.NullOr(ScopeIdSchema),
2528 slot: AuthArtifactSlotSchema,
2629 placementsTemplateJson: Schema.String,
2730 expiresAt: Schema.NullOr(TimestampMsSchema),
packages/platform/sdk/src/schema/models/code-migration.ts+6−2
@@ -1,6 +1,10 @@
1−import { Schema } from "effect";
1+import {
2+ Schema,
3+} from "effect";
24
3−import { TimestampMsSchema } from "../common";
5+import {
6+ TimestampMsSchema,
7+} from "../common";
48
59 export const StoredCodeMigrationRecordSchema = Schema.Struct({
610 id: Schema.String,
packages/platform/sdk/src/schema/models/execution.ts+9−6
@@ -1,12 +1,15 @@
1−import { Schema } from "effect";
1+import {
2+ Schema,
3+} from "effect";
24
3−import { TimestampMsSchema } from "../common";
45 import {
5− AccountIdSchema,
6+ TimestampMsSchema,
7+} from "../common";
8+import {
69 ExecutionIdSchema,
710 ExecutionInteractionIdSchema,
811 ExecutionStepIdSchema,
9− WorkspaceIdSchema,
12+ ScopeIdSchema,
1013 } from "../ids";
1114
1215 export const ExecutionStatusSchema = Schema.Literal(
@@ -20,8 +23,8 @@ export const ExecutionStatusSchema = Schema.Literal( · 7 unmodified lines
2023
2124 export const ExecutionSchema = Schema.Struct({
2225 id: ExecutionIdSchema,
23− workspaceId: WorkspaceIdSchema,
24− createdByAccountId: AccountIdSchema,
26+ scopeId: ScopeIdSchema,
27+ createdByScopeId: ScopeIdSchema,
2528 status: ExecutionStatusSchema,
2629 code: Schema.String,
2730 resultJson: Schema.NullOr(Schema.String),
packages/platform/sdk/src/schema/models/local-config.ts+6−2
@@ -1,6 +1,10 @@
1−import { Schema } from "effect";
1+import {
2+ Schema,
3+} from "effect";
24
3−import { localConfigurableSourceAdapters } from "../../runtime/sources/source-adapters";
5+import {
6+ localConfigurableSourceAdapters,
7+} from "../../runtime/sources/source-adapters";
48
59 export const LocalExecutorRuntimeSchema = Schema.Literal(
610 "quickjs",
packages/platform/sdk/src/schema/models/local-installation.ts+4−4
@@ -1,13 +1,13 @@
11 import * as Schema from "effect/Schema";
22
33 import {
4− AccountIdSchema,
5− WorkspaceIdSchema,
4+ ScopeIdSchema,
65 } from "../ids";
76
87 export const LocalInstallationSchema = Schema.Struct({
9− accountId: AccountIdSchema,
10− workspaceId: WorkspaceIdSchema,
8+ scopeId: ScopeIdSchema,
9+ actorScopeId: ScopeIdSchema,
10+ resolutionScopeIds: Schema.Array(ScopeIdSchema),
1111 });
1212
1313 export const LocalInstallationInsertSchema = LocalInstallationSchema;
packages/platform/sdk/src/schema/models/policy.ts+22−15
@@ -1,28 +1,35 @@
1−import { Schema } from "effect";
1+import {
2+ Schema,
3+} from "effect";
24
3−import { TimestampMsSchema } from "../common";
4−import { PolicyIdSchema, WorkspaceIdSchema } from "../ids";
5+import {
6+ TimestampMsSchema,
7+} from "../common";
8+import {
9+ PolicyIdSchema,
10+ ScopeIdSchema,
11+} from "../ids";
512
6−export const LocalWorkspacePolicyEffectSchema = Schema.Literal("allow", "deny");
7−export const LocalWorkspacePolicyApprovalModeSchema = Schema.Literal("auto", "required");
13+export const LocalScopePolicyEffectSchema = Schema.Literal("allow", "deny");
14+export const LocalScopePolicyApprovalModeSchema = Schema.Literal("auto", "required");
815
9−export const LocalWorkspacePolicySchema = Schema.Struct({
16+export const LocalScopePolicySchema = Schema.Struct({
1017 id: PolicyIdSchema,
1118 key: Schema.String,
12− workspaceId: WorkspaceIdSchema,
19+ scopeId: ScopeIdSchema,
1320 resourcePattern: Schema.String,
14− effect: LocalWorkspacePolicyEffectSchema,
15− approvalMode: LocalWorkspacePolicyApprovalModeSchema,
21+ effect: LocalScopePolicyEffectSchema,
22+ approvalMode: LocalScopePolicyApprovalModeSchema,
1623 priority: Schema.Number,
1724 enabled: Schema.Boolean,
1825 createdAt: TimestampMsSchema,
1926 updatedAt: TimestampMsSchema,
2027 });
2128
22−export const LocalWorkspacePolicyInsertSchema = LocalWorkspacePolicySchema;
23−export const LocalWorkspacePolicyUpdateSchema = Schema.partial(LocalWorkspacePolicySchema);
29+export const LocalScopePolicyInsertSchema = LocalScopePolicySchema;
30+export const LocalScopePolicyUpdateSchema = Schema.partial(LocalScopePolicySchema);
2431
25−export type LocalWorkspacePolicyEffect = typeof LocalWorkspacePolicyEffectSchema.Type;
26−export type LocalWorkspacePolicyApprovalMode =
27− typeof LocalWorkspacePolicyApprovalModeSchema.Type;
28−export type LocalWorkspacePolicy = typeof LocalWorkspacePolicySchema.Type;
32+export type LocalScopePolicyEffect = typeof LocalScopePolicyEffectSchema.Type;
33+export type LocalScopePolicyApprovalMode =
34+ typeof LocalScopePolicyApprovalModeSchema.Type;
35+export type LocalScopePolicy = typeof LocalScopePolicySchema.Type;
packages/platform/sdk/src/schema/models/provider-auth-grant.ts+11−8
@@ -1,11 +1,14 @@
1−import { Schema } from "effect";
1+import {
2+ Schema,
3+} from "effect";
24
3−import { TimestampMsSchema } from "../common";
45 import {
5− AccountIdSchema,
6+ TimestampMsSchema,
7+} from "../common";
8+import {
9+ ScopeIdSchema,
610 ProviderAuthGrantIdSchema,
7− WorkspaceIdSchema,
8− WorkspaceOauthClientIdSchema,
11+ ScopeOauthClientIdSchema,
912 } from "../ids";
1013
1114 import {
@@ -15,10 +18,10 @@ import { · 3 unmodified lines
1518
1619 export const ProviderAuthGrantSchema = Schema.Struct({
1720 id: ProviderAuthGrantIdSchema,
18− workspaceId: WorkspaceIdSchema,
19− actorAccountId: Schema.NullOr(AccountIdSchema),
21+ scopeId: ScopeIdSchema,
22+ actorScopeId: Schema.NullOr(ScopeIdSchema),
2023 providerKey: Schema.String,
21− oauthClientId: WorkspaceOauthClientIdSchema,
24+ oauthClientId: ScopeOauthClientIdSchema,
2225 tokenEndpoint: Schema.String,
2326 clientAuthentication: OAuth2ClientAuthenticationMethodSchema,
2427 headerName: Schema.String,
packages/platform/sdk/src/schema/models/scope-oauth-client.tsrenamed from packages/platform/sdk/src/schema/models/workspace-oauth-client.ts+12−8
@@ -1,14 +1,18 @@
1−import { Schema } from "effect";
1+import {
2+ Schema,
3+} from "effect";
24
3−import { TimestampMsSchema } from "../common";
45 import {
5− WorkspaceIdSchema,
6− WorkspaceOauthClientIdSchema,
6+ TimestampMsSchema,
7+} from "../common";
8+import {
9+ ScopeIdSchema,
10+ ScopeOauthClientIdSchema,
711 } from "../ids";
812
9−export const WorkspaceOauthClientSchema = Schema.Struct({
10− id: WorkspaceOauthClientIdSchema,
11− workspaceId: WorkspaceIdSchema,
13+export const ScopeOauthClientSchema = Schema.Struct({
14+ id: ScopeOauthClientIdSchema,
15+ scopeId: ScopeIdSchema,
1216 providerKey: Schema.String,
1317 label: Schema.NullOr(Schema.String),
1418 clientId: Schema.String,
@@ -19,4 +23,4 @@ export const WorkspaceOauthClientSchema = Schema.Struct({ · 4 unmodified lines
1923 updatedAt: TimestampMsSchema,
2024 });
2125
22−export type WorkspaceOauthClient = typeof WorkspaceOauthClientSchema.Type;
26+export type ScopeOauthClient = typeof ScopeOauthClientSchema.Type;
packages/platform/sdk/src/schema/models/secret-material.ts+9−3
@@ -1,7 +1,13 @@
1−import { Schema } from "effect";
1+import {
2+ Schema,
3+} from "effect";
24
3−import { TimestampMsSchema } from "../common";
4−import { SecretMaterialIdSchema } from "../ids";
5+import {
6+ TimestampMsSchema,
7+} from "../common";
8+import {
9+ SecretMaterialIdSchema,
10+} from "../ids";
511
612 export const SecretMaterialPurposeSchema = Schema.Literal(
713 "auth_material",
packages/platform/sdk/src/schema/models/source-auth-session.ts+11−8
@@ -1,14 +1,17 @@
1−import { Schema } from "effect";
1+import {
2+ Schema,
3+} from "effect";
24
3−import { TimestampMsSchema } from "../common";
45 import {
5− AccountIdSchema,
6+ TimestampMsSchema,
7+} from "../common";
8+import {
9+ ScopeIdSchema,
610 ExecutionIdSchema,
711 ExecutionInteractionIdSchema,
812 SourceIdSchema,
913 SourceAuthSessionIdSchema,
10− WorkspaceOauthClientIdSchema,
11− WorkspaceIdSchema,
14+ ScopeOauthClientIdSchema,
1215 } from "../ids";
1316 import {
1417 CredentialSlotSchema,
@@ -109,7 +112,7 @@ export const ProviderOauthBatchSourceAuthSessionDataSchema = Schema.Struct({ · 94 unmodified lines
109112 authorizationEndpoint: Schema.String,
110113 tokenEndpoint: Schema.String,
111114 redirectUri: Schema.String,
112− oauthClientId: WorkspaceOauthClientIdSchema,
115+ oauthClientId: ScopeOauthClientIdSchema,
113116 clientAuthentication: OAuth2ClientAuthenticationMethodSchema,
114117 scopes: Schema.Array(Schema.String),
115118 headerName: Schema.String,
@@ -129,9 +132,9 @@ export const ProviderOauthBatchSourceAuthSessionDataJsonSchema = Schema.parseJso · 13 unmodified lines
129132
130133 export const SourceAuthSessionSchema = Schema.Struct({
131134 id: SourceAuthSessionIdSchema,
132− workspaceId: WorkspaceIdSchema,
135+ scopeId: ScopeIdSchema,
133136 sourceId: SourceIdSchema,
134− actorAccountId: Schema.NullOr(AccountIdSchema),
137+ actorScopeId: Schema.NullOr(ScopeIdSchema),
135138 credentialSlot: CredentialSlotSchema,
136139 executionId: Schema.NullOr(ExecutionIdSchema),
137140 interactionId: Schema.NullOr(ExecutionInteractionIdSchema),
packages/platform/sdk/src/schema/models/source-catalog.ts+7−3
@@ -1,6 +1,10 @@
1−import { Schema } from "effect";
1+import {
2+ Schema,
3+} from "effect";
24
3−import { TimestampMsSchema } from "../common";
5+import {
6+ TimestampMsSchema,
7+} from "../common";
48 import {
59 SourceCatalogIdSchema,
610 SourceCatalogRevisionIdSchema,
@@ -15,7 +19,7 @@ export const SourceCatalogAdapterKeySchema = Schema.String; · 8 unmodified lines
1519
1620 export const SourceCatalogVisibilitySchema = Schema.Literal(
1721 "private",
18− "workspace",
22+ "scope",
1923 "organization",
2024 "public",
2125 );
packages/platform/sdk/src/schema/models/source-inspection.ts+6−2
@@ -1,6 +1,10 @@
1−import { Schema } from "effect";
1+import {
2+ Schema,
3+} from "effect";
24
3−import { SourceSchema } from "./source";
5+import {
6+ SourceSchema,
7+} from "./source";
48
59 export const SourceInspectionPipelineKindSchema = Schema.Literal("ir");
610
packages/platform/sdk/src/schema/models/source-oauth-client.ts+22−18
@@ -1,31 +1,35 @@
1−import { Schema } from "effect";
1+import {
2+ Schema,
3+} from "effect";
24 export {
35 SourceOauthClientInputSchema,
4− WorkspaceSourceOauthClientRedirectModeSchema,
6+ ScopedSourceOauthClientRedirectModeSchema,
57 } from "@executor/source-core";
68 import {
79 SourceOauthClientInputSchema,
8− WorkspaceSourceOauthClientRedirectModeSchema,
10+ ScopedSourceOauthClientRedirectModeSchema,
911 } from "@executor/source-core";
1012
11−import { TimestampMsSchema } from "../common";
13+import {
14+ TimestampMsSchema,
15+} from "../common";
1216 import {
1317 SourceIdSchema,
14− WorkspaceIdSchema,
15− WorkspaceSourceOauthClientIdSchema,
18+ ScopeIdSchema,
19+ ScopedSourceOauthClientIdSchema,
1620 } from "../ids";
1721
18−export const WorkspaceSourceOauthClientMetadataSchema = Schema.Struct({
19− redirectMode: Schema.optional(WorkspaceSourceOauthClientRedirectModeSchema),
22+export const ScopedSourceOauthClientMetadataSchema = Schema.Struct({
23+ redirectMode: Schema.optional(ScopedSourceOauthClientRedirectModeSchema),
2024 });
2125
22−export const WorkspaceSourceOauthClientMetadataJsonSchema = Schema.parseJson(
23− WorkspaceSourceOauthClientMetadataSchema,
26+export const ScopedSourceOauthClientMetadataJsonSchema = Schema.parseJson(
27+ ScopedSourceOauthClientMetadataSchema,
2428 );
2529
26−export const WorkspaceSourceOauthClientSchema = Schema.Struct({
27− id: WorkspaceSourceOauthClientIdSchema,
28− workspaceId: WorkspaceIdSchema,
30+export const ScopedSourceOauthClientSchema = Schema.Struct({
31+ id: ScopedSourceOauthClientIdSchema,
32+ scopeId: ScopeIdSchema,
2933 sourceId: SourceIdSchema,
3034 providerKey: Schema.String,
3135 clientId: Schema.String,
@@ -36,9 +40,9 @@ export const WorkspaceSourceOauthClientSchema = Schema.Struct({ · 4 unmodified lines
3640 updatedAt: TimestampMsSchema,
3741 });
3842
39−export type WorkspaceSourceOauthClient = typeof WorkspaceSourceOauthClientSchema.Type;
40−export type WorkspaceSourceOauthClientRedirectMode =
41− typeof WorkspaceSourceOauthClientRedirectModeSchema.Type;
42−export type WorkspaceSourceOauthClientMetadata =
43− typeof WorkspaceSourceOauthClientMetadataSchema.Type;
43+export type ScopedSourceOauthClient = typeof ScopedSourceOauthClientSchema.Type;
44+export type ScopedSourceOauthClientRedirectMode =
45+ typeof ScopedSourceOauthClientRedirectModeSchema.Type;
46+export type ScopedSourceOauthClientMetadata =
47+ typeof ScopedSourceOauthClientMetadataSchema.Type;
4448 export type SourceOauthClientInput = typeof SourceOauthClientInputSchema.Type;
packages/platform/sdk/src/schema/models/source.ts+18−10
@@ -1,4 +1,6 @@
1−import { Schema } from "effect";
1+import {
2+ Schema,
3+} from "effect";
24 export {
35 SourceImportAuthPolicySchema,
46 SourceTransportSchema,
@@ -10,16 +12,22 @@ import { · 5 unmodified lines
1012 StringMapSchema,
1113 } from "@executor/source-core";
1214
13−import { TimestampMsSchema } from "../common";
15+import {
16+ TimestampMsSchema,
17+} from "../common";
1418 import {
1519 ProviderAuthGrantIdSchema,
1620 SourceIdSchema,
1721 SourceCatalogIdSchema,
1822 SourceCatalogRevisionIdSchema,
19− WorkspaceIdSchema,
23+ ScopeIdSchema,
2024 } from "../ids";
21−import { SecretRefSchema } from "./auth-artifact";
22−import { JsonObjectSchema } from "./source-auth-session";
25+import {
26+ SecretRefSchema,
27+} from "./auth-artifact";
28+import {
29+ JsonObjectSchema,
30+} from "./source-auth-session";
2331
2432 export const SourceKindSchema = Schema.String;
2533
@@ -91,7 +99,7 @@ export const SourceBindingSchema = Schema.Struct({ · 65 unmodified lines
9199 });
92100
93101 const SourceStorageRowSchema = Schema.Struct({
94− workspaceId: WorkspaceIdSchema,
102+ scopeId: ScopeIdSchema,
95103 sourceId: SourceIdSchema,
96104 catalogId: SourceCatalogIdSchema,
97105 catalogRevisionId: SourceCatalogRevisionIdSchema,
@@ -113,7 +121,7 @@ export const StoredSourceRecordSchema = Schema.transform( · 15 unmodified lines
113121 SourceStorageRowSchema,
114122 Schema.Struct({
115123 id: SourceIdSchema,
116− workspaceId: WorkspaceIdSchema,
124+ scopeId: ScopeIdSchema,
117125 catalogId: SourceCatalogIdSchema,
118126 catalogRevisionId: SourceCatalogRevisionIdSchema,
119127 name: Schema.String,
@@ -133,7 +141,7 @@ export const StoredSourceRecordSchema = Schema.transform( · 13 unmodified lines
133141 strict: false,
134142 decode: (row) => ({
135143 id: row.sourceId,
136− workspaceId: row.workspaceId,
144+ scopeId: row.scopeId,
137145 catalogId: row.catalogId,
138146 catalogRevisionId: row.catalogRevisionId,
139147 name: row.name,
@@ -150,7 +158,7 @@ export const StoredSourceRecordSchema = Schema.transform( · 10 unmodified lines
150158 updatedAt: row.updatedAt,
151159 }),
152160 encode: (source) => ({
153− workspaceId: source.workspaceId,
161+ scopeId: source.scopeId,
154162 sourceId: source.id,
155163 catalogId: source.catalogId,
156164 catalogRevisionId: source.catalogRevisionId,
@@ -172,7 +180,7 @@ export const StoredSourceRecordSchema = Schema.transform( · 15 unmodified lines
172180
173181 export const SourceSchema = Schema.Struct({
174182 id: SourceIdSchema,
175− workspaceId: WorkspaceIdSchema,
183+ scopeId: ScopeIdSchema,
176184 name: Schema.String,
177185 kind: SourceKindSchema,
178186 endpoint: Schema.String,
packages/platform/sdk/src/schema/schema.test.ts+5−1
@@ -1,4 +1,8 @@
1−import { describe, expect, it } from "@effect/vitest";
1+import {
2+ describe,
3+ expect,
4+ it,
5+} from "@effect/vitest";
26 import * as Either from "effect/Either";
37 import * as Schema from "effect/Schema";
48
packages/platform/sdk/src/scope.ts+17−0
@@ -0,0 +1,17 @@
1+import type {
2+ ScopeId,
3+} from "./schema";
4+
5+export type ExecutorScopeDescriptor = {
6+ scopeName: string;
7+ scopeRoot?: string | null;
8+ actorScopeId?: ScopeId | null;
9+ resolutionScopeIds?: ReadonlyArray<ScopeId>;
10+ metadata?: Readonly<Record<string, unknown>>;
11+};
12+
13+export type ExecutorScopeContext = ExecutorScopeDescriptor & {
14+ scopeId: ScopeId;
15+ actorScopeId: ScopeId | null;
16+ resolutionScopeIds: ReadonlyArray<ScopeId>;
17+};
packages/platform/sdk/src/sources/contracts.ts+11−11
@@ -12,9 +12,9 @@ import { · 11 unmodified lines
1212 SourceSchema,
1313 SourceStatusSchema,
1414 SourceOauthClientInputSchema,
15− WorkspaceIdSchema,
16− WorkspaceOauthClientIdSchema,
17− WorkspaceOauthClientSchema,
15+ ScopeIdSchema,
16+ ScopeOauthClientIdSchema,
17+ ScopeOauthClientSchema,
1818 } from "../schema";
1919 import {
2020 ConnectSourcePayloadSchema,
@@ -84,20 +84,20 @@ export const CredentialOauthCompleteUrlParamsSchema = Schema.Struct({ · 63 unmodified lines
8484 error_description: Schema.optional(Schema.String),
8585 });
8686
87−export const WorkspaceOauthClientQuerySchema = Schema.Struct({
87+export const ScopeOauthClientQuerySchema = Schema.Struct({
8888 providerKey: Schema.String,
8989 });
9090
91−export const CreateWorkspaceOauthClientPayloadSchema = Schema.Struct({
91+export const CreateScopeOauthClientPayloadSchema = Schema.Struct({
9292 providerKey: Schema.String,
9393 label: Schema.optional(Schema.NullOr(Schema.String)),
9494 oauthClient: SourceOauthClientInputSchema,
9595 });
9696
97−export type CreateWorkspaceOauthClientPayload =
98− typeof CreateWorkspaceOauthClientPayloadSchema.Type;
97+export type CreateScopeOauthClientPayload =
98+ typeof CreateScopeOauthClientPayloadSchema.Type;
9999
100−export const oauthClientIdParam = WorkspaceOauthClientIdSchema;
100+export const oauthClientIdParam = ScopeOauthClientIdSchema;
101101 export const grantIdParam = ProviderAuthGrantIdSchema;
102102
103103 const ConnectGoogleDiscoveryBatchSourceSchema = Schema.Struct({
@@ -110,7 +110,7 @@ const ConnectGoogleDiscoveryBatchSourceSchema = Schema.Struct({ · 6 unmodified lines
110110 });
111111
112112 export const ConnectSourceBatchPayloadSchema = Schema.Struct({
113− workspaceOauthClientId: WorkspaceOauthClientIdSchema,
113+ scopeOauthClientId: ScopeOauthClientIdSchema,
114114 sources: Schema.Array(ConnectGoogleDiscoveryBatchSourceSchema),
115115 });
116116
@@ -165,6 +165,6 @@ export { · 48 unmodified lines
165165 ConnectSourcePayloadSchema,
166166 type ConnectSourcePayload,
167167 SourceDiscoveryResultSchema,
168− WorkspaceIdSchema,
169− WorkspaceOauthClientSchema,
168+ ScopeIdSchema,
169+ ScopeOauthClientSchema,
170170 };
packages/platform/sdk/src/sources/discovery.ts+3−1
@@ -9,7 +9,9 @@ import { · 8 unmodified lines
99 type SourceDiscoveryResult,
1010 } from "@executor/source-core";
1111
12−import { builtInSourceAdapters } from "../runtime/sources/source-adapters";
12+import {
13+ builtInSourceAdapters,
14+} from "../runtime/sources/source-adapters";
1315
1416 type DiscoverableSourceAdapter = SourceAdapter & {
1517 detectSource: NonNullable<SourceAdapter["detectSource"]>;
packages/platform/sdk/src/sources/inspection.ts+25−19
@@ -8,7 +8,7 @@ import type { · 7 unmodified lines
88 SourceInspectionToolDetail,
99 SourceInspectionToolListItem,
1010 SourceInspectionToolSummary,
11− WorkspaceId,
11+ ScopeId,
1212 } from "../schema";
1313 import {
1414 ControlPlaneNotFoundError,
@@ -16,8 +16,12 @@ import { · 1 unmodified line
1616 } from "../errors";
1717 import * as Effect from "effect/Effect";
1818
19−import { LocalSourceArtifactMissingError } from "../runtime/workspace-errors";
20−import { operationErrors } from "../runtime/policy/operation-errors";
19+import {
20+ LocalSourceArtifactMissingError,
21+} from "../runtime/scope-errors";
22+import {
23+ operationErrors,
24+} from "../runtime/policy/operation-errors";
2125 import {
2226 buildLoadedSourceCatalogToolContract,
2327 expandCatalogToolByPath,
@@ -25,7 +29,9 @@ import { · 1 unmodified line
2529 loadSourceWithCatalog,
2630 type LoadedSourceCatalogTool,
2731 } from "../runtime/catalog/source/runtime";
28−import { RuntimeSourceStoreService } from "../runtime/sources/source-store";
32+import {
33+ RuntimeSourceStoreService,
34+} from "../runtime/sources/source-store";
2935
3036 const sourceInspectOps = {
3137 bundle: operationErrors("sources.inspect.bundle"),
@@ -46,14 +52,14 @@ const canInspectSourceWithoutCatalog = (source: Source): boolean => · 14 unmodified lines
4652 source.status === "auth_required";
4753
4854 const loadSourceForMissingCatalog = (input: {
49− workspaceId: WorkspaceId;
55+ scopeId: ScopeId;
5056 sourceId: SourceId;
5157 cause: LocalSourceArtifactMissingError;
5258 }) =>
5359 Effect.gen(function* () {
5460 const sourceStore = yield* RuntimeSourceStoreService;
5561 const source = yield* sourceStore.loadSourceById({
56− workspaceId: input.workspaceId,
62+ scopeId: input.scopeId,
5763 sourceId: input.sourceId,
5864 });
5965
@@ -65,11 +71,11 @@ const loadSourceForMissingCatalog = (input: { · 5 unmodified lines
6571 });
6672
6773 const loadSourceCatalogOrEmpty = (input: {
68− workspaceId: WorkspaceId;
74+ scopeId: ScopeId;
6975 sourceId: SourceId;
7076 }) =>
7177 loadSourceWithCatalog({
72− workspaceId: input.workspaceId,
78+ scopeId: input.scopeId,
7379 sourceId: input.sourceId,
7480 }).pipe(
7581 Effect.map((catalogEntry) => ({
@@ -79,7 +85,7 @@ const loadSourceCatalogOrEmpty = (input: { · 3 unmodified lines
7985 Effect.catchTag("LocalSourceArtifactMissingError", (cause) =>
8086 Effect.gen(function* () {
8187 const source = yield* loadSourceForMissingCatalog({
82− workspaceId: input.workspaceId,
88+ scopeId: input.scopeId,
8389 sourceId: input.sourceId,
8490 cause,
8591 });
@@ -273,14 +279,14 @@ export const inspectionToolDetailFromTool = ( · 187 unmodified lines
273279 });
274280
275281 const resolveSourceInspection = (input: {
276− workspaceId: WorkspaceId;
282+ scopeId: ScopeId;
277283 sourceId: SourceId;
278284 includeSchemas: boolean;
279285 includeTypePreviews: boolean;
280286 }) =>
281287 Effect.gen(function* () {
282288 const loaded = yield* loadSourceCatalogOrEmpty({
283− workspaceId: input.workspaceId,
289+ scopeId: input.scopeId,
284290 sourceId: input.sourceId,
285291 });
286292
@@ -308,13 +314,13 @@ const resolveSourceInspection = (input: { · 21 unmodified lines
308314 });
309315
310316 const resolveSourceInspectionTool = (input: {
311− workspaceId: WorkspaceId;
317+ scopeId: ScopeId;
312318 sourceId: SourceId;
313319 toolPath: string;
314320 }) =>
315321 Effect.gen(function* () {
316322 const loaded = yield* loadSourceCatalogOrEmpty({
317− workspaceId: input.workspaceId,
323+ scopeId: input.scopeId,
318324 sourceId: input.sourceId,
319325 });
320326
@@ -425,7 +431,7 @@ const mapInspectionError = ( · 104 unmodified lines
425431 };
426432
427433 export const getSourceInspection = (input: {
428− workspaceId: WorkspaceId;
434+ scopeId: ScopeId;
429435 sourceId: SourceId;
430436 }) =>
431437 Effect.gen(function* () {
@@ -456,13 +462,13 @@ export const getSourceInspection = (input: { · 24 unmodified lines
456462 );
457463
458464 export const getSourceInspectionToolDetail = (input: {
459− workspaceId: WorkspaceId;
465+ scopeId: ScopeId;
460466 sourceId: SourceId;
461467 toolPath: string;
462468 }) =>
463469 Effect.gen(function* () {
464470 const inspection = yield* resolveSourceInspectionTool({
465− workspaceId: input.workspaceId,
471+ scopeId: input.scopeId,
466472 sourceId: input.sourceId,
467473 toolPath: input.toolPath,
468474 });
@@ -471,7 +477,7 @@ export const getSourceInspectionToolDetail = (input: { · 2 unmodified lines
471477 if (!tool) {
472478 return yield* sourceInspectOps.tool.notFound(
473479 "Tool not found",
474− `workspaceId=${input.workspaceId} sourceId=${input.sourceId} path=${input.toolPath}`,
480+ `scopeId=${input.scopeId} sourceId=${input.sourceId} path=${input.toolPath}`,
475481 );
476482 }
477483
@@ -487,13 +493,13 @@ export const getSourceInspectionToolDetail = (input: { · 9 unmodified lines
487493 );
488494
489495 export const discoverSourceInspectionTools = (input: {
490− workspaceId: WorkspaceId;
496+ scopeId: ScopeId;
491497 sourceId: SourceId;
492498 payload: SourceInspectionDiscoverPayload;
493499 }) =>
494500 Effect.gen(function* () {
495501 const inspection = yield* resolveSourceInspection({
496− workspaceId: input.workspaceId,
502+ scopeId: input.scopeId,
497503 sourceId: input.sourceId,
498504 includeSchemas: false,
499505 includeTypePreviews: false,
packages/platform/sdk/src/sources/operations.ts+43−34
@@ -3,11 +3,10 @@ import type { · 2 unmodified lines
33 UpdateSourcePayload,
44 } from "./contracts";
55 import {
6− type AccountId,
6+ type ScopeId,
77 SourceIdSchema,
88 type Source,
99 type SourceId,
10− type WorkspaceId,
1110 } from "../schema";
1211 import * as Either from "effect/Either";
1312 import * as Effect from "effect/Effect";
@@ -16,15 +15,25 @@ import { · 2 unmodified lines
1615 createSourceFromPayload,
1716 updateSourceFromPayload,
1817 } from "../runtime/sources/source-definitions";
19−import { getSourceAdapterForSource } from "../runtime/sources/source-adapters";
20−import { mapPersistenceError } from "../runtime/policy/operations-shared";
21−import { operationErrors } from "../runtime/policy/operation-errors";
18+import {
19+ getSourceAdapterForSource,
20+} from "../runtime/sources/source-adapters";
21+import {
22+ mapPersistenceError,
23+} from "../runtime/policy/operations-shared";
24+import {
25+ operationErrors,
26+} from "../runtime/policy/operation-errors";
2227 import {
2328 ExecutorStateStore,
2429 type ExecutorStateStoreShape,
2530 } from "../runtime/executor-state-store";
26−import { RuntimeSourceCatalogSyncService } from "../runtime/catalog/source/sync";
27−import { RuntimeSourceStoreService } from "../runtime/sources/source-store";
31+import {
32+ RuntimeSourceCatalogSyncService,
33+} from "../runtime/catalog/source/sync";
34+import {
35+ RuntimeSourceStoreService,
36+} from "../runtime/sources/source-store";
2837
2938 const sourceOps = {
3039 list: operationErrors("sources.list"),
@@ -41,7 +50,7 @@ const syncArtifactsForSource = (input: { · 10 unmodified lines
4150 store: ExecutorStateStoreShape;
4251 sourceStore: Effect.Effect.Success<typeof RuntimeSourceStoreService>;
4352 source: Source;
44− actorAccountId: AccountId;
53+ actorScopeId: ScopeId;
4554 operation: typeof sourceOps.create | typeof sourceOps.update;
4655 }) =>
4756 Effect.gen(function* () {
@@ -59,7 +68,7 @@ const syncArtifactsForSource = (input: { · 11 unmodified lines
5968 const synced = yield* Effect.either(
6069 catalogSyncService.sync({
6170 source: sourceForSync,
62− actorAccountId: input.actorAccountId,
71+ actorScopeId: input.actorScopeId,
6372 }),
6473 );
6574
@@ -82,7 +91,7 @@ const syncArtifactsForSource = (input: { · 16 unmodified lines
8291 yield* mapPersistenceError(
8392 input.operation.child("source_connected"),
8493 input.sourceStore.persistSource(connectedSource, {
85− actorAccountId: input.actorAccountId,
94+ actorScopeId: input.actorScopeId,
8695 }),
8796 );
8897 return connectedSource;
@@ -114,7 +123,7 @@ const syncArtifactsForSource = (input: { · 25 unmodified lines
114123 yield* mapPersistenceError(
115124 input.operation.child("source_error"),
116125 input.sourceStore.persistSource(erroredSource, {
117− actorAccountId: input.actorAccountId,
126+ actorScopeId: input.actorScopeId,
118127 }),
119128 );
120129 }
@@ -128,16 +137,16 @@ const syncArtifactsForSource = (input: { · 7 unmodified lines
128137 });
129138
130139 export const listSources = (input: {
131− workspaceId: WorkspaceId;
132− accountId: AccountId;
140+ scopeId: ScopeId;
141+ actorScopeId: ScopeId;
133142 }) =>
134143 Effect.flatMap(ExecutorStateStore, () =>
135144 Effect.gen(function* () {
136145 const sourceStore = yield* RuntimeSourceStoreService;
137146
138147 return yield* sourceStore
139− .loadSourcesInWorkspace(input.workspaceId, {
140− actorAccountId: input.accountId,
148+ .loadSourcesInScope(input.scopeId, {
149+ actorScopeId: input.actorScopeId,
141150 })
142151 .pipe(
143152 Effect.mapError((error) =>
@@ -151,8 +160,8 @@ export const listSources = (input: { · 7 unmodified lines
151160 );
152161
153162 export const createSource = (input: {
154− workspaceId: WorkspaceId;
155− accountId: AccountId;
163+ scopeId: ScopeId;
164+ actorScopeId: ScopeId;
156165 payload: CreateSourcePayload;
157166 }) =>
158167 Effect.flatMap(ExecutorStateStore, (store) =>
@@ -161,7 +170,7 @@ export const createSource = (input: { · 2 unmodified lines
161170 const now = Date.now();
162171
163172 const source = yield* createSourceFromPayload({
164− workspaceId: input.workspaceId,
173+ scopeId: input.scopeId,
165174 sourceId: SourceIdSchema.make(`src_${crypto.randomUUID()}`),
166175 payload: input.payload,
167176 now,
@@ -177,7 +186,7 @@ export const createSource = (input: { · 9 unmodified lines
177186 const persistedSource = yield* mapPersistenceError(
178187 sourceOps.create.child("persist"),
179188 sourceStore.persistSource(source, {
180− actorAccountId: input.accountId,
189+ actorScopeId: input.actorScopeId,
181190 }),
182191 );
183192
@@ -185,7 +194,7 @@ export const createSource = (input: { · 1 unmodified line
185194 store,
186195 sourceStore,
187196 source: persistedSource,
188− actorAccountId: input.accountId,
197+ actorScopeId: input.actorScopeId,
189198 operation: sourceOps.create,
190199 });
191200
@@ -194,9 +203,9 @@ export const createSource = (input: { · 2 unmodified lines
194203 );
195204
196205 export const getSource = (input: {
197− workspaceId: WorkspaceId;
206+ scopeId: ScopeId;
198207 sourceId: SourceId;
199− accountId: AccountId;
208+ actorScopeId: ScopeId;
200209 }) =>
201210 Effect.flatMap(ExecutorStateStore, () =>
202211 Effect.gen(function* () {
@@ -204,9 +213,9 @@ export const getSource = (input: { · 1 unmodified line
204213
205214 return yield* sourceStore
206215 .loadSourceById({
207− workspaceId: input.workspaceId,
216+ scopeId: input.scopeId,
208217 sourceId: input.sourceId,
209− actorAccountId: input.accountId,
218+ actorScopeId: input.actorScopeId,
210219 })
211220 .pipe(
212221 Effect.mapError((cause) =>
@@ -214,7 +223,7 @@ export const getSource = (input: { · 1 unmodified line
214223 cause.message.startsWith("Source not found:")
215224 ? sourceOps.get.notFound(
216225 "Source not found",
217− `workspaceId=${input.workspaceId} sourceId=${input.sourceId}`,
226+ `scopeId=${input.scopeId} sourceId=${input.sourceId}`,
218227 )
219228 : sourceOps.get.unknownStorage(
220229 cause,
@@ -226,9 +235,9 @@ export const getSource = (input: { · 5 unmodified lines
226235 );
227236
228237 export const updateSource = (input: {
229− workspaceId: WorkspaceId;
238+ scopeId: ScopeId;
230239 sourceId: SourceId;
231− accountId: AccountId;
240+ actorScopeId: ScopeId;
232241 payload: UpdateSourcePayload;
233242 }) =>
234243 Effect.flatMap(ExecutorStateStore, (store) =>
@@ -236,9 +245,9 @@ export const updateSource = (input: { · 1 unmodified line
236245 const sourceStore = yield* RuntimeSourceStoreService;
237246 const existingSource = yield* sourceStore
238247 .loadSourceById({
239− workspaceId: input.workspaceId,
248+ scopeId: input.scopeId,
240249 sourceId: input.sourceId,
241− actorAccountId: input.accountId,
250+ actorScopeId: input.actorScopeId,
242251 })
243252 .pipe(
244253 Effect.mapError((cause) =>
@@ -246,7 +255,7 @@ export const updateSource = (input: { · 1 unmodified line
246255 cause.message.startsWith("Source not found:")
247256 ? sourceOps.update.notFound(
248257 "Source not found",
249− `workspaceId=${input.workspaceId} sourceId=${input.sourceId}`,
258+ `scopeId=${input.scopeId} sourceId=${input.sourceId}`,
250259 )
251260 : sourceOps.update.unknownStorage(
252261 cause,
@@ -271,7 +280,7 @@ export const updateSource = (input: { · 18 unmodified lines
271280 const persistedSource = yield* mapPersistenceError(
272281 sourceOps.update.child("persist"),
273282 sourceStore.persistSource(updatedSource, {
274− actorAccountId: input.accountId,
283+ actorScopeId: input.actorScopeId,
275284 }),
276285 );
277286
@@ -279,7 +288,7 @@ export const updateSource = (input: { · 1 unmodified line
279288 store,
280289 sourceStore,
281290 source: persistedSource,
282− actorAccountId: input.accountId,
291+ actorScopeId: input.actorScopeId,
283292 operation: sourceOps.update,
284293 });
285294
@@ -288,7 +297,7 @@ export const updateSource = (input: { · 2 unmodified lines
288297 );
289298
290299 export const removeSource = (input: {
291− workspaceId: WorkspaceId;
300+ scopeId: ScopeId;
292301 sourceId: SourceId;
293302 }) =>
294303 Effect.flatMap(ExecutorStateStore, () =>
@@ -297,7 +306,7 @@ export const removeSource = (input: { · 2 unmodified lines
297306 const removed = yield* mapPersistenceError(
298307 sourceOps.remove.child("remove"),
299308 sourceStore.removeSourceById({
300− workspaceId: input.workspaceId,
309+ scopeId: input.scopeId,
301310 sourceId: input.sourceId,
302311 }),
303312 );
packages/platform/sdk/src/workspace.ts+0−15
@@ -1,15 +0,0 @@
1−import type {
2− AccountId,
3− WorkspaceId,
4−} from "./schema";
5−
6−export type ExecutorWorkspaceDescriptor = {
7− workspaceName: string;
8− workspaceRoot?: string | null;
9− metadata?: Readonly<Record<string, unknown>>;
10−};
11−
12−export type ExecutorWorkspaceContext = ExecutorWorkspaceDescriptor & {
13− workspaceId: WorkspaceId;
14− accountId: AccountId;
15−};
packages/sources/core/src/source-models.ts+6−6
@@ -134,7 +134,7 @@ export const SourceBindingSchema = Schema.Struct({ · 133 unmodified lines
134134
135135 export const SourceSchema = Schema.Struct({
136136 id: Schema.String,
137− workspaceId: Schema.String,
137+ scopeId: Schema.String,
138138 name: Schema.String,
139139 kind: SourceKindSchema,
140140 endpoint: Schema.String,
@@ -157,19 +157,19 @@ export const StoredSourceRecordSchema = Schema.Struct({ · 16 unmodified lines
157157 bindingConfigJson: Schema.NullOr(Schema.String),
158158 });
159159
160−export const WorkspaceSourceOauthClientRedirectModeSchema = Schema.Literal(
160+export const ScopedSourceOauthClientRedirectModeSchema = Schema.Literal(
161161 "app_callback",
162162 "loopback",
163163 );
164164
165−export const WorkspaceOauthClientIdSchema = Schema.String;
165+export const ScopeOauthClientIdSchema = Schema.String;
166166
167167 export const SourceOauthClientInputSchema = Schema.Struct({
168168 clientId: Schema.Trim.pipe(Schema.nonEmptyString()),
169169 clientSecret: Schema.optional(
170170 Schema.NullOr(Schema.Trim.pipe(Schema.nonEmptyString())),
171171 ),
172− redirectMode: Schema.optional(WorkspaceSourceOauthClientRedirectModeSchema),
172+ redirectMode: Schema.optional(ScopedSourceOauthClientRedirectModeSchema),
173173 });
174174
175175 export type SecretRef = typeof SecretRefSchema.Type;
@@ -185,6 +185,6 @@ export type SourceAuth = typeof SourceAuthSchema.Type; · 9 unmodified lines
185185 export type SourceBinding = typeof SourceBindingSchema.Type;
186186 export type Source = typeof SourceSchema.Type;
187187 export type StoredSourceRecord = typeof StoredSourceRecordSchema.Type;
188−export type WorkspaceSourceOauthClientRedirectMode =
189− typeof WorkspaceSourceOauthClientRedirectModeSchema.Type;
188+export type ScopedSourceOauthClientRedirectMode =
189+ typeof ScopedSourceOauthClientRedirectModeSchema.Type;
190190 export type SourceOauthClientInput = typeof SourceOauthClientInputSchema.Type;
packages/sources/google-discovery/src/adapter.test.ts+1−1
@@ -81,7 +81,7 @@ const withJsonServer = async <T>(handler: (input: { · 80 unmodified lines
8181
8282 const makeGoogleDiscoverySource = (discoveryUrl: string): Source => ({
8383 id: "src_google_gmail",
84− workspaceId: "ws_test",
84+ scopeId: "ws_test",
8585 name: "Gmail",
8686 kind: "google_discovery",
8787 endpoint: discoveryUrl,
packages/sources/google-discovery/src/adapter.ts+3−3
@@ -38,7 +38,7 @@ import { · 37 unmodified lines
3838 OptionalNullableStringSchema,
3939 SourceCredentialRequiredError,
4040 StringMapSchema,
41− WorkspaceOauthClientIdSchema,
41+ ScopeOauthClientIdSchema,
4242 createCatalogImportMetadata,
4343 EXECUTABLE_BINDING_VERSION,
4444 sourceCoreEffectError,
@@ -60,8 +60,8 @@ const GoogleDiscoveryConnectPayloadSchema = Schema.extend( · 15 unmodified lines
6060 scopes: Schema.optional(
6161 Schema.Array(Schema.Trim.pipe(Schema.nonEmptyString())),
6262 ),
63− workspaceOauthClientId: Schema.optional(
64− Schema.NullOr(WorkspaceOauthClientIdSchema),
63+ scopeOauthClientId: Schema.optional(
64+ Schema.NullOr(ScopeOauthClientIdSchema),
6565 ),
6666 oauthClient: ConnectOauthClientSchema,
6767 name: OptionalNullableStringSchema,
tools/oxlint/README.md+2−0
@@ -14,6 +14,8 @@ Current rules: · 13 unmodified lines
1414 - `executor-monorepo/no-cross-workspace-relative-imports`
1515 - `executor-monorepo/no-direct-effect-tag-read`
1616 - `executor-monorepo/no-effect-run-in-effect-vitest-tests`
17+- `executor-monorepo/no-manual-httpapi-openapi-bridge`
18+- `executor-monorepo/no-manual-openapi-documents`
1719 - `executor-monorepo/no-node-fs-with-effect-imports`
1820 - `executor-monorepo/no-raw-effect-fail-errors`
1921 - `executor-monorepo/no-yield-effect-fail`
tools/oxlint/plugin.mjs+4−0
@@ -4,6 +4,8 @@ import noCrossWorkspaceRelativeImports from "./rules/no-cross-workspace-relative · 3 unmodified lines
44 import noAsyncEffectVitestTests from "./rules/no-async-effect-vitest-tests.mjs";
55 import noDirectEffectTagRead from "./rules/no-direct-effect-tag-read.mjs";
66 import noEffectRunInEffectVitestTests from "./rules/no-effect-run-in-effect-vitest-tests.mjs";
7+import noManualHttpApiOpenApiBridge from "./rules/no-manual-httpapi-openapi-bridge.mjs";
8+import noManualOpenApiDocuments from "./rules/no-manual-openapi-documents.mjs";
79 import noNodeFsWithEffectImports from "./rules/no-node-fs-with-effect-imports.mjs";
810 import noRawEffectFailErrors from "./rules/no-raw-effect-fail-errors.mjs";
911 import noYieldEffectFail from "./rules/no-yield-effect-fail.mjs";
@@ -18,6 +20,8 @@ export default definePlugin({ · 8 unmodified lines
1820 "no-cross-workspace-relative-imports": noCrossWorkspaceRelativeImports,
1921 "no-direct-effect-tag-read": noDirectEffectTagRead,
2022 "no-effect-run-in-effect-vitest-tests": noEffectRunInEffectVitestTests,
23+ "no-manual-httpapi-openapi-bridge": noManualHttpApiOpenApiBridge,
24+ "no-manual-openapi-documents": noManualOpenApiDocuments,
2125 "no-node-fs-with-effect-imports": noNodeFsWithEffectImports,
2226 "no-raw-effect-fail-errors": noRawEffectFailErrors,
2327 "no-yield-effect-fail": noYieldEffectFail,
tools/oxlint/rules/no-manual-httpapi-openapi-bridge.mjs+65−0
@@ -0,0 +1,65 @@
1+import { readFileSync } from "node:fs";
2+import { defineRule } from "@oxlint/plugins";
3+
4+const isTestLikeFile = (filename) =>
5+ filename.includes(".test.")
6+ || filename.includes(".spec.")
7+ || filename.endsWith(".test.ts")
8+ || filename.endsWith(".test.tsx")
9+ || filename.endsWith(".spec.ts")
10+ || filename.endsWith(".spec.tsx");
11+
12+const readsLikeOpenApiHttpApiTest = (filename) => {
13+ try {
14+ const sourceText = readFileSync(filename, "utf8");
15+ return sourceText.includes("HttpApiBuilder.middlewareOpenApi(")
16+ || sourceText.includes("OpenApi.fromApi(");
17+ } catch {
18+ return false;
19+ }
20+};
21+
22+const isHttpApiBuilderCall = (node, methodName) =>
23+ node?.type === "CallExpression"
24+ && node.callee?.type === "MemberExpression"
25+ && node.callee.computed === false
26+ && node.callee.object?.type === "Identifier"
27+ && node.callee.object.name === "HttpApiBuilder"
28+ && node.callee.property?.type === "Identifier"
29+ && node.callee.property.name === methodName;
30+
31+export default defineRule({
32+ meta: {
33+ type: "problem",
34+ docs: {
35+ description:
36+ "Disallow manual Node/Web bridges around Effect HttpApi when serving OpenAPI test servers.",
37+ recommended: true,
38+ },
39+ messages: {
40+ noManualHttpApiOpenApiBridge:
41+ "Do not serve OpenAPI test servers by manually bridging `HttpApiBuilder.toWebHandler(...)` to Node HTTP. Use `@executor/effect-test-utils` (`startOpenApiTestServer` / `makeOpenApiTestServer`) instead.",
42+ },
43+ },
44+ create(context) {
45+ if (
46+ !isTestLikeFile(context.filename)
47+ || !readsLikeOpenApiHttpApiTest(context.filename)
48+ ) {
49+ return {};
50+ }
51+
52+ return {
53+ CallExpression(node) {
54+ if (!isHttpApiBuilderCall(node, "toWebHandler")) {
55+ return;
56+ }
57+
58+ context.report({
59+ node,
60+ messageId: "noManualHttpApiOpenApiBridge",
61+ });
62+ },
63+ };
64+ },
65+});
tools/oxlint/rules/no-manual-openapi-documents.mjs+108−0
@@ -0,0 +1,108 @@
1+import path from "node:path";
2+import { defineRule } from "@oxlint/plugins";
3+
4+const MANUAL_OPENAPI_RULE_ALLOWED_PATH_SEGMENTS = [
5+ `${path.sep}packages${path.sep}sources${path.sep}openapi${path.sep}`,
6+ `${path.sep}packages${path.sep}platform${path.sep}sdk${path.sep}src${path.sep}runtime${path.sep}sources${path.sep}source-adapter-fixture-matrix.test.ts`,
7+ `${path.sep}packages${path.sep}platform${path.sep}sdk${path.sep}src${path.sep}runtime${path.sep}sources${path.sep}source-discovery.test.ts`,
8+];
9+
10+const OPENAPI_MARKER_KEYS = new Set(["openapi", "swagger"]);
11+const OPENAPI_SHAPE_KEYS = new Set([
12+ "components",
13+ "info",
14+ "paths",
15+ "responses",
16+ "security",
17+ "servers",
18+ "tags",
19+]);
20+
21+const isTestLikeFile = (filename) =>
22+ filename.includes(".test.")
23+ || filename.includes(".spec.")
24+ || filename.endsWith(".test.ts")
25+ || filename.endsWith(".test.tsx")
26+ || filename.endsWith(".spec.ts")
27+ || filename.endsWith(".spec.tsx");
28+
29+const isAllowedFile = (filename) =>
30+ MANUAL_OPENAPI_RULE_ALLOWED_PATH_SEGMENTS.some((segment) =>
31+ filename.includes(segment)
32+ );
33+
34+const readPropertyKey = (node) => {
35+ if (!node || node.type !== "Property" || node.computed) {
36+ return null;
37+ }
38+
39+ const key = node.key;
40+ if (!key) {
41+ return null;
42+ }
43+
44+ if (key.type === "Identifier") {
45+ return key.name;
46+ }
47+
48+ if (key.type === "Literal" && typeof key.value === "string") {
49+ return key.value;
50+ }
51+
52+ return null;
53+};
54+
55+const objectExpressionKeys = (node) => {
56+ if (!node || node.type !== "ObjectExpression") {
57+ return [];
58+ }
59+
60+ return node.properties
61+ .map(readPropertyKey)
62+ .filter((key) => typeof key === "string");
63+};
64+
65+const looksLikeOpenApiDocument = (node) => {
66+ if (!node || node.type !== "ObjectExpression") {
67+ return false;
68+ }
69+
70+ const keys = new Set(objectExpressionKeys(node));
71+ const hasOpenApiMarker = [...OPENAPI_MARKER_KEYS].some((key) => keys.has(key));
72+ const hasOpenApiShape = [...OPENAPI_SHAPE_KEYS].some((key) => keys.has(key));
73+
74+ return hasOpenApiMarker && hasOpenApiShape;
75+};
76+
77+export default defineRule({
78+ meta: {
79+ type: "problem",
80+ docs: {
81+ description:
82+ "Disallow hand-authored inline OpenAPI documents in normal test files.",
83+ recommended: true,
84+ },
85+ messages: {
86+ noManualOpenApiDocuments:
87+ "Do not hand-author inline OpenAPI documents here. Use Effect's HttpApi/OpenApi builders, such as `OpenApi.fromApi(...)`, instead. Raw OpenAPI fixtures should live only in dedicated OpenAPI parser/import tests.",
88+ },
89+ },
90+ create(context) {
91+ if (!isTestLikeFile(context.filename) || isAllowedFile(context.filename)) {
92+ return {};
93+ }
94+
95+ return {
96+ ObjectExpression(node) {
97+ if (!looksLikeOpenApiDocument(node)) {
98+ return;
99+ }
100+
101+ context.report({
102+ node,
103+ messageId: "noManualOpenApiDocuments",
104+ });
105+ },
106+ };
107+ },
108+});