@mastra/factory 0.21.1-alpha.2 → 0.22.0-alpha.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (180) hide show
  1. package/dist/capabilities/version-control.d.ts +13 -8
  2. package/dist/capabilities/version-control.d.ts.map +1 -1
  3. package/dist/environment/build-runner.d.ts +72 -0
  4. package/dist/environment/build-runner.d.ts.map +1 -0
  5. package/dist/environment/build-runner.js +131 -0
  6. package/dist/environment/build-runner.js.map +1 -0
  7. package/dist/environment/build-schedule.d.ts +40 -0
  8. package/dist/environment/build-schedule.d.ts.map +1 -0
  9. package/dist/environment/build-schedule.js +94 -0
  10. package/dist/environment/build-schedule.js.map +1 -0
  11. package/dist/environment/build-workflow.d.ts +62 -0
  12. package/dist/environment/build-workflow.d.ts.map +1 -0
  13. package/dist/environment/build-workflow.js +149 -0
  14. package/dist/environment/build-workflow.js.map +1 -0
  15. package/dist/environment/build.d.ts +111 -0
  16. package/dist/environment/build.d.ts.map +1 -0
  17. package/dist/environment/build.js +344 -0
  18. package/dist/environment/build.js.map +1 -0
  19. package/dist/environment/heads.d.ts +36 -0
  20. package/dist/environment/heads.d.ts.map +1 -0
  21. package/dist/environment/heads.js +60 -0
  22. package/dist/environment/heads.js.map +1 -0
  23. package/dist/factory.d.ts +18 -4
  24. package/dist/factory.d.ts.map +1 -1
  25. package/dist/factory.js +89 -15
  26. package/dist/factory.js.map +1 -1
  27. package/dist/index.d.ts +2 -1
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +3 -2
  30. package/dist/integrations/base.d.ts +17 -2
  31. package/dist/integrations/base.d.ts.map +1 -1
  32. package/dist/integrations/github/commits.d.ts +11 -0
  33. package/dist/integrations/github/commits.d.ts.map +1 -1
  34. package/dist/integrations/github/commits.js +20 -1
  35. package/dist/integrations/github/commits.js.map +1 -1
  36. package/dist/integrations/github/config.d.ts +3 -3
  37. package/dist/integrations/github/config.d.ts.map +1 -1
  38. package/dist/integrations/github/config.js +1 -1
  39. package/dist/integrations/github/config.js.map +1 -1
  40. package/dist/integrations/github/routes.d.ts +2 -2
  41. package/dist/integrations/github/routes.d.ts.map +1 -1
  42. package/dist/integrations/github/routes.js +6 -5
  43. package/dist/integrations/github/routes.js.map +1 -1
  44. package/dist/integrations/github/rules.d.ts +3 -1
  45. package/dist/integrations/github/rules.d.ts.map +1 -1
  46. package/dist/integrations/github/rules.js +31 -1
  47. package/dist/integrations/github/rules.js.map +1 -1
  48. package/dist/integrations/github/sandbox.d.ts +35 -0
  49. package/dist/integrations/github/sandbox.d.ts.map +1 -1
  50. package/dist/integrations/github/sandbox.js +203 -2
  51. package/dist/integrations/github/sandbox.js.map +1 -1
  52. package/dist/integrations/github/session-subscriptions.d.ts.map +1 -1
  53. package/dist/integrations/github/session-subscriptions.js +117 -62
  54. package/dist/integrations/github/session-subscriptions.js.map +1 -1
  55. package/dist/integrations/github/token-refresh.d.ts +7 -0
  56. package/dist/integrations/github/token-refresh.d.ts.map +1 -1
  57. package/dist/integrations/github/token-refresh.js.map +1 -1
  58. package/dist/integrations/github/webhook.js.map +1 -1
  59. package/dist/integrations/gitlab/agent-tools.js +1 -1
  60. package/dist/integrations/gitlab/routes.d.ts +2 -2
  61. package/dist/integrations/gitlab/routes.d.ts.map +1 -1
  62. package/dist/integrations/gitlab/routes.js.map +1 -1
  63. package/dist/integrations/gitlab/session-subscriptions.d.ts.map +1 -1
  64. package/dist/integrations/gitlab/session-subscriptions.js +8 -7
  65. package/dist/integrations/gitlab/session-subscriptions.js.map +1 -1
  66. package/dist/integrations/incidentio/agent-tools.js +1 -1
  67. package/dist/integrations/jira/agent-tools.js +1 -1
  68. package/dist/integrations/linear/agent-tools.js +1 -1
  69. package/dist/integrations/platform/github/integration.d.ts.map +1 -1
  70. package/dist/integrations/platform/github/integration.js +46 -2
  71. package/dist/integrations/platform/github/integration.js.map +1 -1
  72. package/dist/integrations/platform/jira/agent-tools.js +1 -1
  73. package/dist/integrations/slack/slack.d.ts.map +1 -1
  74. package/dist/integrations/slack/slack.js +15 -16
  75. package/dist/integrations/slack/slack.js.map +1 -1
  76. package/dist/integrations/subscription-session.js +1 -1
  77. package/dist/packages/_internals/workspace/dist/index.js +21 -1
  78. package/dist/packages/_internals/workspace/dist/index.js.map +1 -1
  79. package/dist/routes/contracts.d.ts +386 -2
  80. package/dist/routes/contracts.d.ts.map +1 -1
  81. package/dist/routes/contracts.js +166 -1
  82. package/dist/routes/contracts.js.map +1 -1
  83. package/dist/routes/fs.d.ts +25 -0
  84. package/dist/routes/fs.d.ts.map +1 -1
  85. package/dist/routes/fs.js +142 -46
  86. package/dist/routes/fs.js.map +1 -1
  87. package/dist/routes/projects.d.ts +6 -0
  88. package/dist/routes/projects.d.ts.map +1 -1
  89. package/dist/routes/projects.js +344 -1
  90. package/dist/routes/projects.js.map +1 -1
  91. package/dist/routes/source-control-sessions.d.ts +2 -0
  92. package/dist/routes/source-control-sessions.d.ts.map +1 -1
  93. package/dist/routes/source-control-sessions.js +36 -1
  94. package/dist/routes/source-control-sessions.js.map +1 -1
  95. package/dist/routes/supervisor.js +1 -1
  96. package/dist/routes/surface.d.ts +6 -3
  97. package/dist/routes/surface.d.ts.map +1 -1
  98. package/dist/routes/surface.js +21 -34
  99. package/dist/routes/surface.js.map +1 -1
  100. package/dist/rules/binding-context.d.ts +1 -1
  101. package/dist/rules/binding-context.d.ts.map +1 -1
  102. package/dist/rules/binding-context.js +1 -1
  103. package/dist/rules/binding-context.js.map +1 -1
  104. package/dist/rules/review-source-tool.js +1 -1
  105. package/dist/rules/start-coordinator.d.ts.map +1 -1
  106. package/dist/rules/start-coordinator.js +7 -2
  107. package/dist/rules/start-coordinator.js.map +1 -1
  108. package/dist/rules/tools.js +1 -1
  109. package/dist/sandbox/callback-factory-sandbox.d.ts +15 -0
  110. package/dist/sandbox/callback-factory-sandbox.d.ts.map +1 -0
  111. package/dist/sandbox/callback-factory-sandbox.js +22 -0
  112. package/dist/sandbox/callback-factory-sandbox.js.map +1 -0
  113. package/dist/sandbox/local-factory-sandbox.d.ts +20 -0
  114. package/dist/sandbox/local-factory-sandbox.d.ts.map +1 -0
  115. package/dist/sandbox/local-factory-sandbox.js +27 -0
  116. package/dist/sandbox/local-factory-sandbox.js.map +1 -0
  117. package/dist/sandbox/session-retirement.d.ts.map +1 -1
  118. package/dist/sandbox/session-retirement.js +23 -3
  119. package/dist/sandbox/session-retirement.js.map +1 -1
  120. package/dist/sandbox/session-sandbox.d.ts +82 -40
  121. package/dist/sandbox/session-sandbox.d.ts.map +1 -1
  122. package/dist/sandbox/session-sandbox.js +130 -13
  123. package/dist/sandbox/session-sandbox.js.map +1 -1
  124. package/dist/sandbox/workdir.d.ts +10 -0
  125. package/dist/sandbox/workdir.d.ts.map +1 -1
  126. package/dist/sandbox/workdir.js +10 -1
  127. package/dist/sandbox/workdir.js.map +1 -1
  128. package/dist/secret-encryption.d.ts.map +1 -1
  129. package/dist/secret-encryption.js +12 -1
  130. package/dist/secret-encryption.js.map +1 -1
  131. package/dist/session/environment-repositories.d.ts +31 -0
  132. package/dist/session/environment-repositories.d.ts.map +1 -0
  133. package/dist/session/environment-repositories.js +89 -0
  134. package/dist/session/environment-repositories.js.map +1 -0
  135. package/dist/session/environment-state-processor.d.ts +59 -0
  136. package/dist/session/environment-state-processor.d.ts.map +1 -0
  137. package/dist/session/environment-state-processor.js +105 -0
  138. package/dist/session/environment-state-processor.js.map +1 -0
  139. package/dist/session/factory-session.d.ts +26 -35
  140. package/dist/session/factory-session.d.ts.map +1 -1
  141. package/dist/session/factory-session.js +34 -71
  142. package/dist/session/factory-session.js.map +1 -1
  143. package/dist/session/filesystem-capture.d.ts +13 -0
  144. package/dist/session/filesystem-capture.d.ts.map +1 -1
  145. package/dist/session/filesystem-capture.js +73 -14
  146. package/dist/session/filesystem-capture.js.map +1 -1
  147. package/dist/session/source-control-tools.d.ts +1 -1
  148. package/dist/session/source-control-tools.d.ts.map +1 -1
  149. package/dist/session/source-control-tools.js +144 -71
  150. package/dist/session/source-control-tools.js.map +1 -1
  151. package/dist/spa-static.js +2 -2
  152. package/dist/storage/domains/audit/agent-audit.d.ts +6 -1
  153. package/dist/storage/domains/audit/agent-audit.d.ts.map +1 -1
  154. package/dist/storage/domains/audit/agent-audit.js +31 -13
  155. package/dist/storage/domains/audit/agent-audit.js.map +1 -1
  156. package/dist/storage/domains/custom-providers/base.d.ts.map +1 -1
  157. package/dist/storage/domains/custom-providers/base.js +13 -2
  158. package/dist/storage/domains/custom-providers/base.js.map +1 -1
  159. package/dist/storage/domains/projects/base.d.ts +115 -2
  160. package/dist/storage/domains/projects/base.d.ts.map +1 -1
  161. package/dist/storage/domains/projects/base.js +219 -12
  162. package/dist/storage/domains/projects/base.js.map +1 -1
  163. package/dist/storage/domains/source-control/base.d.ts +123 -7
  164. package/dist/storage/domains/source-control/base.d.ts.map +1 -1
  165. package/dist/storage/domains/source-control/base.js +406 -33
  166. package/dist/storage/domains/source-control/base.js.map +1 -1
  167. package/dist/storage/domains/source-control/inmemory.d.ts +19 -6
  168. package/dist/storage/domains/source-control/inmemory.d.ts.map +1 -1
  169. package/dist/storage/domains/source-control/inmemory.js +90 -2
  170. package/dist/storage/domains/source-control/inmemory.js.map +1 -1
  171. package/dist/storage/domains/work-items/base.d.ts.map +1 -1
  172. package/dist/storage/domains/work-items/base.js +12 -5
  173. package/dist/storage/domains/work-items/base.js.map +1 -1
  174. package/dist/supervisor/read-tools.js +1 -1
  175. package/dist/supervisor/write-tools.js +1 -1
  176. package/dist/workspace.d.ts +50 -7
  177. package/dist/workspace.d.ts.map +1 -1
  178. package/dist/workspace.js +344 -24
  179. package/dist/workspace.js.map +1 -1
  180. package/package.json +7 -7
@@ -2,8 +2,8 @@ import { FACTORY_RULE_STAGES, factoryRuleStage } from "../rules/types.js";
2
2
  import { factoryDispatchFailureMetadata } from "../rules/dispatch-errors.js";
3
3
  import { decisionRetryable, overtakenDecisionIds } from "../rules/decision-applicability.js";
4
4
  import { runFactoryHealthCheck } from "./health.js";
5
- import { createTool } from "@mastra/core/tools";
6
5
  import { z } from "zod";
6
+ import { createTool } from "@mastra/core/tools";
7
7
  //#region src/supervisor/read-tools.ts
8
8
  const MAX_TEXT = 600;
9
9
  const MAX_ERROR = 400;
@@ -3,8 +3,8 @@ import { boardForWorkItem } from "../boards/semantics.js";
3
3
  import "../boards/index.js";
4
4
  import { factoryDispatchFailureMetadata } from "../rules/dispatch-errors.js";
5
5
  import { overtakenDecisionIds } from "../rules/decision-applicability.js";
6
- import { createTool } from "@mastra/core/tools";
7
6
  import { z } from "zod";
7
+ import { createTool } from "@mastra/core/tools";
8
8
  //#region src/supervisor/write-tools.ts
9
9
  function createFactorySupervisorWriteTools(deps) {
10
10
  const now = deps.now ?? (() => /* @__PURE__ */ new Date());
@@ -1,11 +1,11 @@
1
1
  import { SandboxFilesystem } from '@mastra/code-sdk/agents/sandbox-filesystem';
2
2
  import type { getDynamicWorkspace } from '@mastra/code-sdk/agents/workspace';
3
3
  import { Workspace } from '@mastra/core/workspace';
4
- import type { SkillSource, SkillSourceEntry, SkillSourceStat, WorkspaceSandbox } from '@mastra/core/workspace';
5
- import type { VersionControl } from './capabilities/version-control.js';
6
- import type { MastraFactorySandboxConfig } from './factory.js';
4
+ import type { FactorySandbox, SkillSource, SkillSourceEntry, SkillSourceStat, WorkspaceSandbox } from '@mastra/core/workspace';
5
+ import type { RepositoryAccess, VersionControl } from './capabilities/version-control.js';
7
6
  import type { GithubIntegration } from './integrations/github/integration.js';
8
- import type { FactoryProjectsStorage } from './storage/domains/projects/base.js';
7
+ import type { FactorySandboxContext } from './sandbox/session-sandbox.js';
8
+ import type { FactoryProject, FactoryProjectsStorage } from './storage/domains/projects/base.js';
9
9
  import type { SourceControlStorageHandle } from './storage/domains/source-control/base.js';
10
10
  import type { WorkItemsStorage } from './storage/domains/work-items/base.js';
11
11
  export declare const BUNDLED_FACTORY_SKILLS_PATH: string;
@@ -51,15 +51,15 @@ export interface WorkspaceSourceControlProvider {
51
51
  /** Stable integration id used to select the provider-owned storage partition. */
52
52
  id: string;
53
53
  /** Provider capability used to resolve fresh repository credentials. */
54
- versionControl: Pick<VersionControl, 'getRepositoryAccess'>;
54
+ versionControl: Pick<VersionControl, 'getRepositoryAccess' | 'getRepositoriesAccess'>;
55
55
  /** Provider-owned source-control rows, including Factory sessions. */
56
56
  storage: SourceControlStorageHandle;
57
57
  /** Present only for GitHub, whose CLI PAT rotation has additional semantics. */
58
58
  github?: GithubIntegration;
59
59
  }
60
60
  export interface CreateWorkspaceFactoryOptions {
61
- /** Factory sandbox runtime config (session sandbox callback). */
62
- sandbox?: MastraFactorySandboxConfig;
61
+ /** The normalized factory sandbox constructing one sandbox per session. */
62
+ sandbox?: FactorySandbox;
63
63
  /** Defaults to `'lazy'`. */
64
64
  sandboxStart?: FactorySandboxStart;
65
65
  /** Source-control providers whose repositories may back Factory sessions. */
@@ -83,6 +83,49 @@ export declare class FactoryWorkspaceRegistry {
83
83
  register(sessionId: string, workspaceId: string, generation: number, unregister: WorkspaceUnregister): Promise<boolean>;
84
84
  invalidateSession(sessionId: string): Promise<void>;
85
85
  }
86
+ /** One environment repository as the sandbox context and the boot need it, in position order. */
87
+ export interface SessionEnvironmentRepo {
88
+ projectRepositoryId: string;
89
+ repositoryId: string;
90
+ slug: string;
91
+ /** The session's base branch: the link's branch when set, else the repository default. */
92
+ defaultBranch: string;
93
+ /** The repository's own default branch, which the template image clones and pins. */
94
+ templateBranch: string;
95
+ position: number;
96
+ setupCommand: string | undefined;
97
+ teardownCommand: string | undefined;
98
+ }
99
+ /** The session's factory environment, resolved from the project and its `inEnvironment` links. */
100
+ export interface SessionEnvironment {
101
+ repos: SessionEnvironmentRepo[];
102
+ workspaceSetupCommand: string | undefined;
103
+ /** Only an absolute root is passed on; the templates reject anything else. */
104
+ workingDirectory: string | undefined;
105
+ /** The project's stored provider settings document, `{}` when nothing was set. */
106
+ settings: Record<string, unknown>;
107
+ }
108
+ /**
109
+ * The project's environment as a session (or an environment build) sees it:
110
+ * every `inEnvironment` link under this source-control integration in
111
+ * position order, plus the project's settings, setup command and workdir.
112
+ */
113
+ export declare function resolveProjectEnvironment(storage: Pick<SourceControlStorageHandle, 'projectRepositories' | 'repositories'>, project: FactoryProject): Promise<SessionEnvironment>;
114
+ /**
115
+ * The context fields the list form of the repo templates needs: one entry per
116
+ * environment repository, each minting its own token at build time, with
117
+ * `getRepositoryAccess` present but undefined (the two are mutually exclusive
118
+ * for the template). Shared by session boot and environment builds so both
119
+ * resolve the same template identity; `resolveHead` is only set when given.
120
+ */
121
+ export declare function environmentSandboxContext(environment: SessionEnvironment, options: {
122
+ orgId: string;
123
+ getRepositoryAccess: (args: {
124
+ orgId: string;
125
+ repositoryId: string;
126
+ }) => Promise<RepositoryAccess>;
127
+ resolveHead?: FactorySandboxContext['resolveHead'];
128
+ }): Omit<FactorySandboxContext, 'sessionId'>;
86
129
  export declare function createWorkspaceFactory(options?: CreateWorkspaceFactoryOptions): ({ requestContext, mastra, skillExtension }: DynamicWorkspaceContext) => Promise<Workspace<import("@mastra/core/workspace").WorkspaceFilesystem | undefined, WorkspaceSandbox | undefined, undefined> | Workspace<SandboxFilesystem, WorkspaceSandbox, undefined> | undefined>;
87
130
  export {};
88
131
  //# sourceMappingURL=workspace.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../src/workspace.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../src/workspace.ts"],"names":[],"mappings":"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"}
package/dist/workspace.js CHANGED
@@ -1,17 +1,20 @@
1
1
  import { timedPhase } from "./timing.js";
2
2
  import { getFactoryAuthOrgId, getFactoryAuthUserFromContext, getFactoryAuthUserId } from "./auth.js";
3
3
  import { mergeRequestNumberFromBranch, pullRequestNumberFromBranch } from "./work-item-branch.js";
4
+ import { redactCredentials } from "./environment/build.js";
4
5
  import { getGithubPat } from "./integrations/github/pat.js";
6
+ import { DEFAULT_COMMAND_TIMEOUT_MS, SetupCommandError, checkoutSessionBranch, materializeRepo, runSetupCommand, runTeardownCommand, syncEnvironmentRepository } from "./integrations/github/sandbox.js";
5
7
  import { registerGithubPatKind, registerGithubRefreshTarget, registerGithubTokenInjector, registerGithubTokenInjectorResolver } from "./integrations/github/token-refresh.js";
6
8
  import { requireExec } from "./sandbox/materialization.js";
7
- import { createSessionSetupHook, evictSessionSandbox, getSessionSandbox, hasFailedSetupCommand, recordFailedSetupCommand, resolveSessionWorkdir } from "./sandbox/session-sandbox.js";
8
- import { DEFAULT_COMMAND_TIMEOUT_MS, SetupCommandError, checkoutSessionBranch, materializeRepo, runSetupCommand, runTeardownCommand } from "./integrations/github/sandbox.js";
9
+ import { repositoryDirectoryName } from "./sandbox/workdir.js";
10
+ import { createEnvironmentSetupHook, createSessionSetupHook, evictSessionSandbox, getSessionSandbox, hasFailedSetupCommand, recordFailedSetupCommand, resolveSessionWorkdir } from "./sandbox/session-sandbox.js";
11
+ import { clearSessionEnvironment, recordSessionEnvironment, setSessionEnvironmentNote } from "./session/environment-state-processor.js";
9
12
  import { parseSupervisorResourceId } from "./supervisor/session.js";
10
- import path, { dirname, join } from "path";
11
13
  import { LocalSkillSource, Workspace } from "@mastra/core/workspace";
12
- import { SandboxFilesystem } from "@mastra/code-sdk/agents/sandbox-filesystem";
13
14
  import { existsSync } from "fs";
15
+ import path, { dirname, join } from "path";
14
16
  import { fileURLToPath } from "url";
17
+ import { SandboxFilesystem } from "@mastra/code-sdk/agents/sandbox-filesystem";
15
18
  import { MASTRACODE_WORKSPACE_TOOLS } from "@mastra/code-sdk/agents/tool-availability";
16
19
  import { DEFAULT_CONFIG_DIR } from "@mastra/code-sdk/constants";
17
20
  //#region src/workspace.ts
@@ -241,6 +244,106 @@ async function resolveSourceControlSession(providers, sessionId) {
241
244
  if (matches.length > 1) throw new Error(`Factory session ${sessionId} is ambiguous across source-control providers`);
242
245
  return matches[0] ?? null;
243
246
  }
247
+ /**
248
+ * Resolve the factory environment a session boots from: the project's
249
+ * settings plus every `inEnvironment` link of the project under this
250
+ * source-control integration, ordered by position. `undefined` when the
251
+ * session carries no factory or the project is gone. With no link in the
252
+ * environment `repos` is empty and the session keeps the single-repository
253
+ * sandbox of its own link, still booted with the project's settings. A link
254
+ * whose repository row is missing is skipped with a warning rather than
255
+ * failing every start of the session.
256
+ */
257
+ async function resolveSessionEnvironment(projects, storage, session) {
258
+ if (!projects || !session.factoryProjectId) return void 0;
259
+ const project = await projects.get({
260
+ orgId: session.orgId,
261
+ id: session.factoryProjectId
262
+ });
263
+ if (!project) return void 0;
264
+ return resolveProjectEnvironment(storage, project);
265
+ }
266
+ /**
267
+ * The project's environment as a session (or an environment build) sees it:
268
+ * every `inEnvironment` link under this source-control integration in
269
+ * position order, plus the project's settings, setup command and workdir.
270
+ */
271
+ async function resolveProjectEnvironment(storage, project) {
272
+ const orgId = project.orgId;
273
+ const links = (await storage.projectRepositories.listByProject({
274
+ orgId,
275
+ factoryProjectId: project.id
276
+ })).filter((link) => link.inEnvironment).sort((a, b) => a.position - b.position || a.createdAt.getTime() - b.createdAt.getTime());
277
+ const repos = [];
278
+ const directories = /* @__PURE__ */ new Set();
279
+ for (const link of links) {
280
+ const repository = await storage.repositories.get({
281
+ orgId,
282
+ id: link.repositoryId
283
+ });
284
+ if (!repository) {
285
+ console.warn("[Mastra Factory] Environment repository link has no repository row; skipping it", {
286
+ orgId,
287
+ factoryProjectId: project.id,
288
+ projectRepositoryId: link.id
289
+ });
290
+ continue;
291
+ }
292
+ const directory = repositoryDirectoryName(repository.slug);
293
+ if (directories.has(directory)) {
294
+ console.warn("[Mastra Factory] Environment repository shares its directory name with an earlier one; skipping it", {
295
+ orgId,
296
+ factoryProjectId: project.id,
297
+ projectRepositoryId: link.id,
298
+ slug: repository.slug,
299
+ directory
300
+ });
301
+ continue;
302
+ }
303
+ directories.add(directory);
304
+ repos.push({
305
+ projectRepositoryId: link.id,
306
+ repositoryId: repository.id,
307
+ slug: repository.slug,
308
+ defaultBranch: link.branch || repository.defaultBranch,
309
+ templateBranch: repository.defaultBranch,
310
+ position: link.position,
311
+ setupCommand: link.setupCommand ?? void 0,
312
+ teardownCommand: link.teardownCommand ?? void 0
313
+ });
314
+ }
315
+ const workdir = project.sandboxWorkingDirectory?.trim();
316
+ return {
317
+ repos,
318
+ workspaceSetupCommand: project.workspaceSetupCommand?.trim() || void 0,
319
+ workingDirectory: workdir?.startsWith("/") ? workdir : void 0,
320
+ settings: project.sandboxSettings ?? {}
321
+ };
322
+ }
323
+ /**
324
+ * The context fields the list form of the repo templates needs: one entry per
325
+ * environment repository, each minting its own token at build time, with
326
+ * `getRepositoryAccess` present but undefined (the two are mutually exclusive
327
+ * for the template). Shared by session boot and environment builds so both
328
+ * resolve the same template identity; `resolveHead` is only set when given.
329
+ */
330
+ function environmentSandboxContext(environment, options) {
331
+ return {
332
+ setupCommand: void 0,
333
+ getRepositoryAccess: void 0,
334
+ repos: environment.repos.map((repo) => ({
335
+ getRepositoryAccess: () => options.getRepositoryAccess({
336
+ orgId: options.orgId,
337
+ repositoryId: repo.repositoryId
338
+ }),
339
+ ...repo.setupCommand ? { setupCommand: repo.setupCommand } : {}
340
+ })),
341
+ ...environment.workspaceSetupCommand ? { workspaceSetupCommand: environment.workspaceSetupCommand } : {},
342
+ continueOnSetupFailure: true,
343
+ ...environment.workingDirectory ? { workingDirectory: environment.workingDirectory } : {},
344
+ ...options.resolveHead ? { resolveHead: options.resolveHead } : {}
345
+ };
346
+ }
244
347
  function createWorkspaceFactory(options = {}) {
245
348
  const { sandbox: sandboxConfig, github, projects, workItems } = options;
246
349
  const sourceControls = options.sourceControls ?? (github ? [{
@@ -276,14 +379,14 @@ function createWorkspaceFactory(options = {}) {
276
379
  const userId = getFactoryAuthUserId(user);
277
380
  if (!user?.organizationId || !userId) throw new Error(`Factory session ${session.sessionId} was resolved without a caller identity`);
278
381
  if (user.organizationId !== session.orgId || session.visibility === "private" && userId !== session.userId) throw new Error(`Factory session ${session.sessionId} is not available to the current user`);
279
- if (!sandboxConfig) throw new Error("A sandbox callback is required to create a Factory session workspace");
280
- const createSessionSandboxInstance = sandboxConfig;
382
+ if (!sandboxConfig) throw new Error("A sandbox is required to create a Factory session workspace");
383
+ const factorySandbox = sandboxConfig;
281
384
  const githubProvider = sourceControl.github;
282
385
  const storage = sourceControl.storage;
283
- const projectRepository = await storage.projectRepositories.get({
386
+ const projectRepository = session.projectRepositoryId ? await storage.projectRepositories.get({
284
387
  orgId: session.orgId,
285
388
  id: session.projectRepositoryId
286
- });
389
+ }) : null;
287
390
  if (!projectRepository) throw new Error(`Repository link ${session.projectRepositoryId} was not found`);
288
391
  const [connection, repository] = await Promise.all([storage.connections.get({
289
392
  orgId: session.orgId,
@@ -298,12 +401,24 @@ function createWorkspaceFactory(options = {}) {
298
401
  id: connection.installationId
299
402
  })) throw new Error(`${sourceControl.id} installation ${connection.installationId} was not found`);
300
403
  const repoFullName = repository.slug;
404
+ const resolvedEnvironment = await resolveSessionEnvironment(projects, storage, session);
405
+ const sandboxSettings = resolvedEnvironment?.settings ?? {};
406
+ const environment = resolvedEnvironment?.repos.some((repo) => repo.projectRepositoryId === projectRepository.id) ? resolvedEnvironment : void 0;
407
+ const tokenRepositoryIds = environment ? [repository.id, ...environment.repos.map((repo) => repo.repositoryId).filter((id) => id !== repository.id)] : [repository.id];
301
408
  if (githubProvider) registerGithubRefreshTarget(requestContext, {
302
409
  orgId: session.orgId,
303
- repositoryId: repository.id
410
+ repositoryId: repository.id,
411
+ ...tokenRepositoryIds.length > 1 ? { mint: () => getRepositoryToken() } : {}
304
412
  });
305
413
  const runSetupOn = (target, workdir, gate) => runSessionSetup(requireExec(target), workdir, gate);
306
- const guardedSetup = createSessionSetupHook(runSetupOn, session.id, repoFullName, projectRepository.setupCommand ?? void 0);
414
+ const runEnvironmentSetupOn = (target, gate) => runSessionEnvironmentSetup(requireExec(target), gate);
415
+ const guardedSetup = environment ? createEnvironmentSetupHook(runEnvironmentSetupOn, session.id, repoFullName, {
416
+ repos: environment.repos.map((repo) => ({
417
+ slug: repo.slug,
418
+ ...repo.setupCommand ? { setupCommand: repo.setupCommand } : {}
419
+ })),
420
+ ...environment.workspaceSetupCommand ? { workspaceSetupCommand: environment.workspaceSetupCommand } : {}
421
+ }) : createSessionSetupHook(runSetupOn, session.id, repoFullName, projectRepository.setupCommand ?? void 0);
307
422
  const setupHook = async (args) => {
308
423
  if (workspaceRegistry.generation(session.sessionId) !== workspaceGeneration) throw retiredError();
309
424
  let setupError;
@@ -319,7 +434,7 @@ function createWorkspaceFactory(options = {}) {
319
434
  await storage.sessions.setSandbox({
320
435
  id: session.id,
321
436
  sandboxId: target.sandboxId ?? null,
322
- sandboxWorkdir: sessionEntry.workdir ?? ""
437
+ sandboxWorkdir: sessionWorkdir(sessionEntry.workdir) ?? ""
323
438
  });
324
439
  constructedWorkspaces.get(workspaceId)?.skills?.refresh().catch(() => {});
325
440
  };
@@ -370,16 +485,21 @@ function createWorkspaceFactory(options = {}) {
370
485
  await finishStart();
371
486
  };
372
487
  const constructSessionEntry = () => getSessionSandbox(session.id, repoFullName, () => {
373
- const sandbox = createSessionSandboxInstance({
488
+ const sandbox = factorySandbox.create({
374
489
  sessionId: session.id,
375
490
  sandboxId: session.sandboxId ?? void 0,
376
491
  repoFullName,
377
- setupCommand: projectRepository.setupCommand ?? void 0,
378
- getRepositoryAccess: () => sourceControl.versionControl.getRepositoryAccess({
492
+ ...environment ? environmentSandboxContext(environment, {
379
493
  orgId: session.orgId,
380
- repositoryId: repository.id
381
- })
382
- });
494
+ getRepositoryAccess: (args) => sourceControl.versionControl.getRepositoryAccess(args)
495
+ }) : {
496
+ setupCommand: projectRepository.setupCommand ?? void 0,
497
+ getRepositoryAccess: () => sourceControl.versionControl.getRepositoryAccess({
498
+ orgId: session.orgId,
499
+ repositoryId: repository.id
500
+ })
501
+ }
502
+ }, sandboxSettings);
383
503
  sandbox.setOnStart((previous) => async (args) => {
384
504
  await timedPhase(`workspace.onStart(${args.outcome})`, async () => {
385
505
  await setupHook(args);
@@ -388,7 +508,7 @@ function createWorkspaceFactory(options = {}) {
388
508
  });
389
509
  startEagerly = eagerSandboxStart;
390
510
  return sandbox;
391
- });
511
+ }, environment?.repos.map((repo) => repo.slug) ?? []);
392
512
  let startEagerly = false;
393
513
  const fireEagerStart = () => {
394
514
  if (!startEagerly) return;
@@ -398,11 +518,15 @@ function createWorkspaceFactory(options = {}) {
398
518
  });
399
519
  };
400
520
  const sessionEntry = constructSessionEntry();
401
- const workdir = sessionEntry.workdir;
521
+ const multiRepo = (environment?.repos.length ?? 0) > 1;
522
+ const sessionWorkdir = (checkout) => checkout === void 0 ? void 0 : multiRepo ? path.posix.dirname(checkout) : checkout;
523
+ const workdir = sessionWorkdir(sessionEntry.workdir);
402
524
  sessionEntry.sandbox.provider;
403
- if (ctx && workdir && ctx.getState()?.projectPath !== workdir) await ctx.setState({
525
+ const checkoutDirs = environment && multiRepo ? [repositoryDirectoryName(repoFullName), ...environment.repos.filter((repo) => repo.slug.toLowerCase() !== repoFullName.toLowerCase()).map((repo) => repositoryDirectoryName(repo.slug))] : void 0;
526
+ if (ctx && workdir && (ctx.getState()?.projectPath !== workdir || ctx.getState()?.instructionRoots?.join("\n") !== checkoutDirs?.join("\n"))) await ctx.setState({
404
527
  projectPath: workdir,
405
- projectName: repoFullName
528
+ projectName: repoFullName,
529
+ instructionRoots: checkoutDirs
406
530
  });
407
531
  const findSessionBinding = async () => {
408
532
  const binding = await workItems.findActiveRunBindingForSession({
@@ -431,7 +555,34 @@ function createWorkspaceFactory(options = {}) {
431
555
  orgId: session.orgId,
432
556
  repositoryId: repository.id
433
557
  });
558
+ let tokenScopeWarned = false;
434
559
  const getRepositoryToken = async () => {
560
+ if (tokenRepositoryIds.length > 1) {
561
+ let reason = "provider mints per-repository tokens only";
562
+ let wide;
563
+ if (sourceControl.versionControl.getRepositoriesAccess) try {
564
+ wide = await sourceControl.versionControl.getRepositoriesAccess({
565
+ orgId: session.orgId,
566
+ repositoryIds: tokenRepositoryIds
567
+ });
568
+ reason = "provider cannot mint one token for this repository set";
569
+ } catch (error) {
570
+ reason = `provider failed to mint one token for this repository set (${error instanceof Error ? error.name : "error"})`;
571
+ }
572
+ if (wide?.authorization?.token) {
573
+ setSessionEnvironmentNote(session.sessionId, null);
574
+ return wide.authorization.token;
575
+ }
576
+ if (!tokenScopeWarned) {
577
+ tokenScopeWarned = true;
578
+ console.warn("[Mastra Factory] GH_TOKEN covers the session repository only", {
579
+ sessionId: session.sessionId,
580
+ repositories: environment?.repos.map((repo) => repo.slug) ?? [repoFullName],
581
+ reason
582
+ });
583
+ }
584
+ setSessionEnvironmentNote(session.sessionId, `GH_TOKEN covers ${repoFullName} only; use the source_control_* tools for other repositories.`);
585
+ }
435
586
  const token = (await getRepositoryAccess()).authorization?.token;
436
587
  if (!token) throw new Error("Repository access did not include a bearer token for the Factory session");
437
588
  return token;
@@ -634,17 +785,185 @@ function createWorkspaceFactory(options = {}) {
634
785
  }
635
786
  }
636
787
  };
788
+ const runSessionEnvironmentSetup = async (target, gate) => {
789
+ const repos = environment.repos;
790
+ const primaryDir = `${gate.root}/${repositoryDirectoryName(repoFullName)}`;
791
+ const states = [];
792
+ const failures = /* @__PURE__ */ new Map();
793
+ const repeated = /* @__PURE__ */ new Set();
794
+ let primaryError;
795
+ const record = () => {
796
+ if (workspaceRegistry.generation(session.sessionId) !== workspaceGeneration) return;
797
+ recordSessionEnvironment(session.sessionId, {
798
+ workingDirectory: gate.root,
799
+ repositories: states
800
+ }, states.flatMap((state) => {
801
+ const command = repos.find((repo) => repo.slug === state.slug)?.teardownCommand;
802
+ return command ? [{
803
+ slug: state.slug,
804
+ dir: state.dir,
805
+ command
806
+ }] : [];
807
+ }));
808
+ };
809
+ let inFlight;
810
+ try {
811
+ for (const repo of repos) {
812
+ inFlight = repo.slug;
813
+ const entry = gate.repos.find((candidate) => candidate.slug === repo.slug);
814
+ if (!entry) continue;
815
+ const isPrimary = repo.projectRepositoryId === session.projectRepositoryId;
816
+ const state = {
817
+ slug: repo.slug,
818
+ dir: entry.dir,
819
+ branch: null,
820
+ defaultBranch: repo.defaultBranch,
821
+ position: repo.position,
822
+ setupStatus: repo.setupCommand ? "ok" : "skipped"
823
+ };
824
+ states.push(state);
825
+ if (isPrimary) {
826
+ try {
827
+ await runSessionSetup(target, primaryDir, entry.gate);
828
+ state.branch = session.branch;
829
+ if (repo.setupCommand && hasFailedSetupCommand(session.id, repo.setupCommand)) {
830
+ state.setupStatus = "failed";
831
+ repeated.add(repo.slug);
832
+ }
833
+ } catch (error) {
834
+ if (!(error instanceof SetupCommandError)) throw error;
835
+ state.branch = session.branch;
836
+ state.setupStatus = "failed";
837
+ failures.set(repo.slug, error.message);
838
+ primaryError = error;
839
+ }
840
+ continue;
841
+ }
842
+ try {
843
+ const access = await sourceControl.versionControl.getRepositoryAccess({
844
+ orgId: session.orgId,
845
+ repositoryId: repo.repositoryId
846
+ });
847
+ const token = access.authorization?.token;
848
+ if (!token) throw new Error(`Repository access for ${repo.slug} did not include a bearer token`);
849
+ await materializeRepo({
850
+ row: {
851
+ id: session.id,
852
+ sandboxWorkdir: entry.dir,
853
+ materializedAt: null
854
+ },
855
+ repoInfo: {
856
+ repoFullName: repo.slug,
857
+ defaultBranch: repo.defaultBranch,
858
+ cloneUrl: access.cloneUrl,
859
+ authUsername: access.authorization?.username
860
+ },
861
+ sandbox: target,
862
+ token,
863
+ storage: { markMaterialized: async () => {} }
864
+ });
865
+ state.branch = (await timedPhase(`workspace.sync(${repo.slug})`, () => syncEnvironmentRepository(target, entry.dir, {
866
+ branch: session.branch,
867
+ defaultBranch: repo.defaultBranch,
868
+ token,
869
+ repoFullName: repo.slug,
870
+ cloneUrl: access.cloneUrl,
871
+ authUsername: access.authorization?.username
872
+ }))).branch;
873
+ } catch (error) {
874
+ console.warn("[Mastra Factory] Environment repository could not be synced; continuing the boot", {
875
+ orgId: session.orgId,
876
+ sessionId: session.sessionId,
877
+ projectRepositoryId: repo.projectRepositoryId,
878
+ error: error instanceof Error ? error.message.slice(-2e3) : String(error)
879
+ });
880
+ state.setupStatus = "failed";
881
+ failures.set(repo.slug, error instanceof Error ? error.message : String(error));
882
+ continue;
883
+ }
884
+ if (!repo.setupCommand || entry.gate.setupDone) continue;
885
+ if (hasFailedSetupCommand(session.id, `${repo.slug}:${repo.setupCommand}`)) {
886
+ state.setupStatus = "failed";
887
+ repeated.add(repo.slug);
888
+ continue;
889
+ }
890
+ try {
891
+ await timedPhase(`workspace.setup(${repo.slug})`, () => runSetupCommand(target, entry.dir, repo.setupCommand));
892
+ await entry.gate.markSetupDone();
893
+ } catch (error) {
894
+ if (!(error instanceof SetupCommandError)) throw error;
895
+ recordFailedSetupCommand(session.id, `${repo.slug}:${repo.setupCommand}`);
896
+ state.setupStatus = "failed";
897
+ failures.set(repo.slug, error.message);
898
+ console.warn("[Mastra Factory] Environment repository setup command failed; continuing the boot", {
899
+ orgId: session.orgId,
900
+ sessionId: session.sessionId,
901
+ projectRepositoryId: repo.projectRepositoryId,
902
+ error: error.message.slice(-2e3)
903
+ });
904
+ }
905
+ }
906
+ inFlight = void 0;
907
+ const workspaceCommand = environment.workspaceSetupCommand;
908
+ if (workspaceCommand && !gate.workspace.setupDone) if (hasFailedSetupCommand(session.id, `workspace:${workspaceCommand}`)) console.warn("[Mastra Factory] Skipping workspace setup command that already failed this session", {
909
+ orgId: session.orgId,
910
+ sessionId: session.sessionId
911
+ });
912
+ else try {
913
+ await timedPhase("workspace.setup(workspace)", () => runSetupCommand(target, gate.root, workspaceCommand));
914
+ await gate.workspace.markSetupDone();
915
+ } catch (error) {
916
+ if (!(error instanceof SetupCommandError)) throw error;
917
+ recordFailedSetupCommand(session.id, `workspace:${workspaceCommand}`);
918
+ throw new SetupCommandError(`${error.message}. The sandbox stays usable: the workspace setup command is skipped for the rest of the session. Retry your command, then fix it in the environment settings or run it manually.`, error.code);
919
+ }
920
+ } finally {
921
+ record();
922
+ await recordSetupResults(repos, states, failures, inFlight, repeated);
923
+ }
924
+ if (primaryError) throw primaryError;
925
+ };
926
+ const recordSetupResults = async (repos, states, failures, inFlight, repeated) => {
927
+ const builtAt = /* @__PURE__ */ new Date();
928
+ try {
929
+ for (const state of states) {
930
+ if (state.setupStatus === "skipped") continue;
931
+ if (state.slug === inFlight && state.setupStatus !== "failed") continue;
932
+ if (repeated.has(state.slug)) continue;
933
+ const repo = repos.find((candidate) => candidate.slug === state.slug);
934
+ if (!repo) continue;
935
+ const error = failures.get(state.slug);
936
+ await storage.projectRepositories.setBuildStatus({
937
+ orgId: session.orgId,
938
+ id: repo.projectRepositoryId,
939
+ status: state.setupStatus === "failed" ? "failed" : "configured",
940
+ error: error === void 0 ? null : redactCredentials(error).slice(-2e3),
941
+ builtAt
942
+ });
943
+ }
944
+ } catch (error) {
945
+ console.warn("[Mastra Factory] Could not record environment setup results", {
946
+ orgId: session.orgId,
947
+ sessionId: session.sessionId,
948
+ error: error instanceof Error ? error.message : String(error)
949
+ });
950
+ }
951
+ };
637
952
  const sessionSandbox = requireExec(sessionEntry.sandbox);
638
953
  const filesystem = new SandboxFilesystem({
639
954
  id: `sandbox-fs:${workspaceId}`,
640
955
  sandbox: sessionSandbox,
641
- workdir: () => resolveSessionWorkdir(session.id, sessionEntry.sandbox, repoFullName)
956
+ workdir: async () => {
957
+ const checkout = await resolveSessionWorkdir(session.id, sessionEntry.sandbox, repoFullName);
958
+ return sessionWorkdir(checkout) ?? checkout;
959
+ }
642
960
  });
643
- const projectSkillPaths = [
961
+ const skillRoots = [
644
962
  path.join(configDir, "skills"),
645
963
  ".claude/skills",
646
964
  ".agents/skills"
647
965
  ];
966
+ const projectSkillPaths = checkoutDirs ? checkoutDirs.flatMap((dir) => skillRoots.map((skillPath) => path.posix.join(dir, skillPath))) : skillRoots;
648
967
  const guardedSkillFallback = new UnmaterializedAwareSkillSource(filesystem, () => sessionEntry.sandbox.status === "running");
649
968
  const workspace = new Workspace({
650
969
  id: workspaceId,
@@ -663,6 +982,7 @@ function createWorkspaceFactory(options = {}) {
663
982
  skillCacheReviewState.delete(workspaceId);
664
983
  skillReviewChecks.delete(workspaceId);
665
984
  evictSessionSandbox(session.id);
985
+ clearSessionEnvironment(session.sessionId);
666
986
  await mastra?.removeWorkspace?.(workspaceId);
667
987
  })) throw new Error(`Factory session ${session.sessionId} was retired during workspace materialization`);
668
988
  fireEagerStart();
@@ -670,6 +990,6 @@ function createWorkspaceFactory(options = {}) {
670
990
  };
671
991
  }
672
992
  //#endregion
673
- export { BUNDLED_FACTORY_SKILLS_PATH, FACTORY_SKILL_NAMES, FactorySkillSource, FactoryWorkspaceRegistry, REVIEW_ONLY_FACTORY_SKILLS, createWorkspaceFactory, rescanFactorySkills, resolveLocalFactorySkillsPath };
993
+ export { BUNDLED_FACTORY_SKILLS_PATH, FACTORY_SKILL_NAMES, FactorySkillSource, FactoryWorkspaceRegistry, REVIEW_ONLY_FACTORY_SKILLS, createWorkspaceFactory, environmentSandboxContext, rescanFactorySkills, resolveLocalFactorySkillsPath, resolveProjectEnvironment };
674
994
 
675
995
  //# sourceMappingURL=workspace.js.map