@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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"callback-factory-sandbox.d.ts","sourceRoot":"","sources":["../../src/sandbox/callback-factory-sandbox.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,KAAK,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAEnF,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,sBAAsB,CAAC;AAEvE;;;;GAIG;AACH,qBAAa,sBAAuB,SAAQ,cAAc,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;;IAC/E,QAAQ,CAAC,QAAQ,YAAY;gBAIjB,QAAQ,EAAE,0BAA0B;IAKhD,MAAM,CAAC,GAAG,EAAE,qBAAqB,GAAG,aAAa;CAGlD"}
@@ -0,0 +1,22 @@
1
+ import { FactorySandbox } from "@mastra/core/workspace";
2
+ //#region src/sandbox/callback-factory-sandbox.ts
3
+ /**
4
+ * Wraps the `sandbox: ctx => new SomeSandbox(...)` host callback
5
+ * as a FactorySandbox. The host owns everything inside the callback, so the
6
+ * factory knows nothing about it: no settings, no template, no builds.
7
+ */
8
+ var CallbackFactorySandbox = class extends FactorySandbox {
9
+ provider = "custom";
10
+ #callback;
11
+ constructor(callback) {
12
+ super();
13
+ this.#callback = callback;
14
+ }
15
+ create(ctx) {
16
+ return this.#callback(ctx);
17
+ }
18
+ };
19
+ //#endregion
20
+ export { CallbackFactorySandbox };
21
+
22
+ //# sourceMappingURL=callback-factory-sandbox.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"callback-factory-sandbox.js","names":["#callback"],"sources":["../../src/sandbox/callback-factory-sandbox.ts"],"sourcesContent":["import { FactorySandbox } from '@mastra/core/workspace';\nimport type { FactorySandboxContext, MastraSandbox } from '@mastra/core/workspace';\n\nimport type { MastraFactorySandboxConfig } from './session-sandbox.js';\n\n/**\n * Wraps the `sandbox: ctx => new SomeSandbox(...)` host callback\n * as a FactorySandbox. The host owns everything inside the callback, so the\n * factory knows nothing about it: no settings, no template, no builds.\n */\nexport class CallbackFactorySandbox extends FactorySandbox<Record<string, never>> {\n readonly provider = 'custom';\n\n readonly #callback: MastraFactorySandboxConfig;\n\n constructor(callback: MastraFactorySandboxConfig) {\n super();\n this.#callback = callback;\n }\n\n create(ctx: FactorySandboxContext): MastraSandbox {\n return this.#callback(ctx);\n }\n}\n"],"mappings":";;;;;;;AAUA,IAAa,yBAAb,cAA4C,eAAsC;CAChF,WAAoB;CAEpB;CAEA,YAAY,UAAsC;EAChD,MAAM;EACN,KAAKA,YAAY;CACnB;CAEA,OAAO,KAA2C;EAChD,OAAO,KAAKA,UAAU,GAAG;CAC3B;AACF"}
@@ -0,0 +1,20 @@
1
+ import { FactorySandbox, LocalSandbox } from '@mastra/core/workspace';
2
+ import type { FactorySandboxContext } from '@mastra/core/workspace';
3
+ export interface LocalFactorySandboxOptions {
4
+ /** Directory under which every session gets its own working directory. */
5
+ root: string;
6
+ /** Environment for commands run in the sandbox. */
7
+ env?: Record<string, string>;
8
+ }
9
+ /**
10
+ * Sessions run on the host machine: each session's sandbox is a LocalSandbox
11
+ * rooted at `join(root, sessionId)`, where the repository checks out as a
12
+ * subdirectory. No template and no user-tunable settings.
13
+ */
14
+ export declare class LocalFactorySandbox extends FactorySandbox<Record<string, never>> {
15
+ #private;
16
+ readonly provider = "local";
17
+ constructor(options: LocalFactorySandboxOptions);
18
+ create(ctx: FactorySandboxContext): LocalSandbox;
19
+ }
20
+ //# sourceMappingURL=local-factory-sandbox.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-factory-sandbox.d.ts","sourceRoot":"","sources":["../../src/sandbox/local-factory-sandbox.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAEpE,MAAM,WAAW,0BAA0B;IACzC,0EAA0E;IAC1E,IAAI,EAAE,MAAM,CAAC;IACb,mDAAmD;IACnD,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9B;AAED;;;;GAIG;AACH,qBAAa,mBAAoB,SAAQ,cAAc,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;;IAC5E,QAAQ,CAAC,QAAQ,WAAW;gBAIhB,OAAO,EAAE,0BAA0B;IAK/C,MAAM,CAAC,GAAG,EAAE,qBAAqB,GAAG,YAAY;CAOjD"}
@@ -0,0 +1,27 @@
1
+ import { FactorySandbox, LocalSandbox } from "@mastra/core/workspace";
2
+ import { join } from "path";
3
+ //#region src/sandbox/local-factory-sandbox.ts
4
+ /**
5
+ * Sessions run on the host machine: each session's sandbox is a LocalSandbox
6
+ * rooted at `join(root, sessionId)`, where the repository checks out as a
7
+ * subdirectory. No template and no user-tunable settings.
8
+ */
9
+ var LocalFactorySandbox = class extends FactorySandbox {
10
+ provider = "local";
11
+ #options;
12
+ constructor(options) {
13
+ super();
14
+ this.#options = options;
15
+ }
16
+ create(ctx) {
17
+ return new LocalSandbox({
18
+ id: ctx.sessionId,
19
+ workingDirectory: join(this.#options.root, ctx.sessionId),
20
+ ...this.#options.env ? { env: this.#options.env } : {}
21
+ });
22
+ }
23
+ };
24
+ //#endregion
25
+ export { LocalFactorySandbox };
26
+
27
+ //# sourceMappingURL=local-factory-sandbox.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-factory-sandbox.js","names":["#options"],"sources":["../../src/sandbox/local-factory-sandbox.ts"],"sourcesContent":["import { join } from 'node:path';\nimport { FactorySandbox, LocalSandbox } from '@mastra/core/workspace';\nimport type { FactorySandboxContext } from '@mastra/core/workspace';\n\nexport interface LocalFactorySandboxOptions {\n /** Directory under which every session gets its own working directory. */\n root: string;\n /** Environment for commands run in the sandbox. */\n env?: Record<string, string>;\n}\n\n/**\n * Sessions run on the host machine: each session's sandbox is a LocalSandbox\n * rooted at `join(root, sessionId)`, where the repository checks out as a\n * subdirectory. No template and no user-tunable settings.\n */\nexport class LocalFactorySandbox extends FactorySandbox<Record<string, never>> {\n readonly provider = 'local';\n\n readonly #options: LocalFactorySandboxOptions;\n\n constructor(options: LocalFactorySandboxOptions) {\n super();\n this.#options = options;\n }\n\n create(ctx: FactorySandboxContext): LocalSandbox {\n return new LocalSandbox({\n id: ctx.sessionId,\n workingDirectory: join(this.#options.root, ctx.sessionId),\n ...(this.#options.env ? { env: this.#options.env } : {}),\n });\n }\n}\n"],"mappings":";;;;;;;;AAgBA,IAAa,sBAAb,cAAyC,eAAsC;CAC7E,WAAoB;CAEpB;CAEA,YAAY,SAAqC;EAC/C,MAAM;EACN,KAAKA,WAAW;CAClB;CAEA,OAAO,KAA0C;EAC/C,OAAO,IAAI,aAAa;GACtB,IAAI,IAAI;GACR,kBAAkB,KAAK,KAAKA,SAAS,MAAM,IAAI,SAAS;GACxD,GAAI,KAAKA,SAAS,MAAM,EAAE,KAAK,KAAKA,SAAS,IAAI,IAAI,CAAC;EACxD,CAAC;CACH;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"session-retirement.d.ts","sourceRoot":"","sources":["../../src/sandbox/session-retirement.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAGV,0BAA0B,EAC3B,MAAM,2CAA2C,CAAC;AACnD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AAI9E,KAAK,aAAa,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;AAEjF,MAAM,WAAW,mCAAmC;IAClD,iBAAiB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAChE,IAAI,CAAC,EAAE,aAAa,CAAC;CACtB;AAED,MAAM,WAAW,kBAAkB;IACjC,aAAa,EAAE,0BAA0B,CAAC;IAC1C,8EAA8E;IAC9E,SAAS,CAAC,EAAE,IAAI,CAAC,gBAAgB,EAAE,wBAAwB,CAAC,CAAC;IAC7D,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,OAAO,CAAC;CACxB;AAQD;;;;;;;;;;GAUG;AACH,qBAAa,4BAA4B;;gBAK3B,OAAO,GAAE,mCAAwC;IAKvD,aAAa,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IAWvD,sBAAsB,CAAC,OAAO,EAAE;QACpC,SAAS,EAAE,IAAI,CAAC,gBAAgB,EAAE,KAAK,CAAC,CAAC;QACzC,aAAa,EAAE,0BAA0B,CAAC;QAC1C,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,EAAE,MAAM,CAAC;KACpB,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBX,+BAA+B,CAAC,OAAO,EAAE;QAC7C,aAAa,EAAE,0BAA0B,CAAC;QAC1C,SAAS,CAAC,EAAE,IAAI,CAAC,gBAAgB,EAAE,wBAAwB,CAAC,CAAC;QAC7D,KAAK,EAAE,MAAM,CAAC;QACd,mBAAmB,EAAE,MAAM,CAAC;KAC7B,GAAG,OAAO,CAAC,IAAI,CAAC;CA0FlB"}
1
+ {"version":3,"file":"session-retirement.d.ts","sourceRoot":"","sources":["../../src/sandbox/session-retirement.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAGV,0BAA0B,EAC3B,MAAM,2CAA2C,CAAC;AACnD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AAI9E,KAAK,aAAa,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;AAEjF,MAAM,WAAW,mCAAmC;IAClD,iBAAiB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAChE,IAAI,CAAC,EAAE,aAAa,CAAC;CACtB;AAED,MAAM,WAAW,kBAAkB;IACjC,aAAa,EAAE,0BAA0B,CAAC;IAC1C,8EAA8E;IAC9E,SAAS,CAAC,EAAE,IAAI,CAAC,gBAAgB,EAAE,wBAAwB,CAAC,CAAC;IAC7D,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,OAAO,CAAC;CACxB;AAQD;;;;;;;;;;GAUG;AACH,qBAAa,4BAA4B;;gBAK3B,OAAO,GAAE,mCAAwC;IAKvD,aAAa,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IAWvD,sBAAsB,CAAC,OAAO,EAAE;QACpC,SAAS,EAAE,IAAI,CAAC,gBAAgB,EAAE,KAAK,CAAC,CAAC;QACzC,aAAa,EAAE,0BAA0B,CAAC;QAC1C,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,EAAE,MAAM,CAAC;KACpB,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBX,+BAA+B,CAAC,OAAO,EAAE;QAC7C,aAAa,EAAE,0BAA0B,CAAC;QAC1C,SAAS,CAAC,EAAE,IAAI,CAAC,gBAAgB,EAAE,wBAAwB,CAAC,CAAC;QAC7D,KAAK,EAAE,MAAM,CAAC;QACd,mBAAmB,EAAE,MAAM,CAAC;KAC7B,GAAG,OAAO,CAAC,IAAI,CAAC;CAqHlB"}
@@ -1,7 +1,8 @@
1
+ import { DEFAULT_COMMAND_TIMEOUT_MS, runTeardownCommand } from "../integrations/github/sandbox.js";
1
2
  import { requireExec } from "./materialization.js";
2
3
  import { peekSessionSandbox } from "./session-sandbox.js";
4
+ import { peekSessionEnvironmentTeardown } from "../session/environment-state-processor.js";
3
5
  import { releaseSessionSandbox } from "../integrations/github/sandbox-release.js";
4
- import { DEFAULT_COMMAND_TIMEOUT_MS, runTeardownCommand } from "../integrations/github/sandbox.js";
5
6
  //#region src/sandbox/session-retirement.ts
6
7
  function boundedError(error) {
7
8
  const detail = error instanceof Error ? error.message : String(error);
@@ -88,12 +89,28 @@ var SessionRetirementCoordinator = class {
88
89
  async #teardownSessionSandbox(input, session) {
89
90
  const entry = peekSessionSandbox(session.id);
90
91
  if (!entry) return;
92
+ const environmentTeardown = peekSessionEnvironmentTeardown(session.sessionId);
93
+ if (environmentTeardown.length > 0) {
94
+ for (const repo of environmentTeardown) try {
95
+ await runTeardownCommand(requireExec(entry.sandbox), repo.dir, repo.command, { timeoutMs: DEFAULT_COMMAND_TIMEOUT_MS });
96
+ } catch (error) {
97
+ this.#warn("Factory teardown command failed", {
98
+ orgId: session.orgId,
99
+ sessionId: session.sessionId,
100
+ projectRepositoryId: session.projectRepositoryId,
101
+ repository: repo.slug,
102
+ error: boundedError(error)
103
+ });
104
+ }
105
+ await this.#releaseSandbox(input, session);
106
+ return;
107
+ }
91
108
  let projectRepository;
92
109
  try {
93
- projectRepository = await input.sourceControl.projectRepositories.get({
110
+ projectRepository = session.projectRepositoryId ? await input.sourceControl.projectRepositories.get({
94
111
  orgId: input.orgId,
95
112
  id: session.projectRepositoryId
96
- });
113
+ }) : null;
97
114
  } catch (error) {
98
115
  this.#warn("Factory repository settings could not be loaded for session retirement", {
99
116
  orgId: session.orgId,
@@ -112,6 +129,9 @@ var SessionRetirementCoordinator = class {
112
129
  error: boundedError(error)
113
130
  });
114
131
  }
132
+ await this.#releaseSandbox(input, session);
133
+ }
134
+ async #releaseSandbox(input, session) {
115
135
  try {
116
136
  await releaseSessionSandbox({
117
137
  sessionId: session.id,
@@ -1 +1 @@
1
- {"version":3,"file":"session-retirement.js","names":["#invalidateSession","#warn","#locks","#retireSession","#teardownSessionSandbox"],"sources":["../../src/sandbox/session-retirement.ts"],"sourcesContent":["import { releaseSessionSandbox } from '../integrations/github/sandbox-release.js';\nimport { DEFAULT_COMMAND_TIMEOUT_MS, runTeardownCommand } from '../integrations/github/sandbox.js';\nimport type {\n ProjectRepository,\n SourceControlSession,\n SourceControlStorageHandle,\n} from '../storage/domains/source-control/base.js';\nimport type { WorkItemsStorage } from '../storage/domains/work-items/base.js';\nimport { requireExec } from './materialization.js';\nimport { peekSessionSandbox } from './session-sandbox.js';\n\ntype WarningLogger = (message: string, details: Record<string, unknown>) => void;\n\nexport interface SessionRetirementCoordinatorOptions {\n invalidateSession?: (sessionId: string) => Promise<void> | void;\n warn?: WarningLogger;\n}\n\nexport interface RetireSessionInput {\n sourceControl: SourceControlStorageHandle;\n /** When provided, deleting the session row also strips its work-item refs. */\n workItems?: Pick<WorkItemsStorage, 'clearSessionReferences'>;\n orgId: string;\n sessionId: string;\n deleteSession: boolean;\n}\n\nfunction boundedError(error: unknown): string {\n const detail = error instanceof Error ? error.message : String(error);\n if (detail.length <= 2000) return detail;\n return `${detail.slice(0, 200)}...${detail.slice(-1797)}`;\n}\n\n/**\n * Owns terminal and destructive session cleanup. Each session is serialized so\n * duplicate role bindings and competing deletion/transition requests cannot\n * run teardown concurrently.\n *\n * Sandbox identity is the session id: retirement stops (or, for deleted\n * sessions, destroys) the sandbox this process holds in the session memo.\n * Sessions opened on another replica have no memo entry here — their VMs are\n * left to the provider's idle lifecycle (pause / idle GC), which is the\n * provider-owned half of the contract.\n */\nexport class SessionRetirementCoordinator {\n readonly #invalidateSession: NonNullable<SessionRetirementCoordinatorOptions['invalidateSession']>;\n readonly #warn: WarningLogger;\n readonly #locks = new Map<string, Promise<void>>();\n\n constructor(options: SessionRetirementCoordinatorOptions = {}) {\n this.#invalidateSession = options.invalidateSession ?? (() => {});\n this.#warn = options.warn ?? ((message, details) => console.warn(`[Mastra Factory] ${message}`, details));\n }\n\n async retireSession(input: RetireSessionInput): Promise<void> {\n const previous = this.#locks.get(input.sessionId) ?? Promise.resolve();\n const current = previous.catch(() => {}).then(() => this.#retireSession(input));\n this.#locks.set(input.sessionId, current);\n try {\n await current;\n } finally {\n if (this.#locks.get(input.sessionId) === current) this.#locks.delete(input.sessionId);\n }\n }\n\n async retireWorkItemSessions(options: {\n workItems: Pick<WorkItemsStorage, 'get'>;\n sourceControl: SourceControlStorageHandle;\n orgId: string;\n workItemId: string;\n }): Promise<void> {\n const item = await options.workItems.get({ orgId: options.orgId, id: options.workItemId });\n if (!item) return;\n const sessionIds = [...new Set(Object.values(item.sessions).map(session => session.sessionId))];\n await Promise.all(\n sessionIds.map(sessionId =>\n this.retireSession({\n sourceControl: options.sourceControl,\n orgId: options.orgId,\n sessionId,\n deleteSession: false,\n }),\n ),\n );\n }\n\n async retireProjectRepositorySessions(options: {\n sourceControl: SourceControlStorageHandle;\n workItems?: Pick<WorkItemsStorage, 'clearSessionReferences'>;\n orgId: string;\n projectRepositoryId: string;\n }): Promise<void> {\n const sessions = await options.sourceControl.sessions.listByProjectRepository({\n projectRepositoryId: options.projectRepositoryId,\n });\n await Promise.all(\n sessions.map(session =>\n this.retireSession({\n sourceControl: options.sourceControl,\n ...(options.workItems ? { workItems: options.workItems } : {}),\n orgId: options.orgId,\n sessionId: session.sessionId,\n deleteSession: true,\n }),\n ),\n );\n }\n\n async #retireSession(input: RetireSessionInput): Promise<void> {\n const session = await input.sourceControl.sessions.getBySessionId(input.sessionId);\n if (!session || session.orgId !== input.orgId) return;\n\n try {\n await this.#teardownSessionSandbox(input, session);\n } finally {\n try {\n await this.#invalidateSession(session.sessionId);\n } catch (error) {\n this.#warn('Factory session workspace cache invalidation failed', {\n orgId: session.orgId,\n sessionId: session.sessionId,\n projectRepositoryId: session.projectRepositoryId,\n error: boundedError(error),\n });\n }\n\n if (input.deleteSession) {\n // Refs first: clearing again is a no-op, but refs on a deleted row would dangle forever.\n await input.workItems?.clearSessionReferences({ orgId: input.orgId, sessionId: input.sessionId });\n await input.sourceControl.sessions.delete(session.id);\n }\n }\n }\n\n async #teardownSessionSandbox(input: RetireSessionInput, session: SourceControlSession): Promise<void> {\n const entry = peekSessionSandbox(session.id);\n if (!entry) return;\n\n let projectRepository: ProjectRepository | null | undefined;\n try {\n projectRepository = await input.sourceControl.projectRepositories.get({\n orgId: input.orgId,\n id: session.projectRepositoryId,\n });\n } catch (error) {\n this.#warn('Factory repository settings could not be loaded for session retirement', {\n orgId: session.orgId,\n sessionId: session.sessionId,\n projectRepositoryId: session.projectRepositoryId,\n error: boundedError(error),\n });\n }\n\n // An unresolved workdir means the sandbox never started in this process:\n // nothing was set up, so there is nothing for a teardown command to undo.\n if (projectRepository?.teardownCommand && entry.workdir) {\n try {\n await runTeardownCommand(requireExec(entry.sandbox), entry.workdir, projectRepository.teardownCommand, {\n timeoutMs: DEFAULT_COMMAND_TIMEOUT_MS,\n });\n } catch (error) {\n this.#warn('Factory teardown command failed', {\n orgId: session.orgId,\n sessionId: session.sessionId,\n projectRepositoryId: session.projectRepositoryId,\n error: boundedError(error),\n });\n }\n }\n\n try {\n await releaseSessionSandbox({ sessionId: session.id, destroy: input.deleteSession });\n } catch (error) {\n this.#warn('Factory session sandbox release failed', {\n orgId: session.orgId,\n sessionId: session.sessionId,\n projectRepositoryId: session.projectRepositoryId,\n error: boundedError(error),\n });\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"session-retirement.js","names":["#invalidateSession","#warn","#locks","#retireSession","#teardownSessionSandbox","#releaseSandbox"],"sources":["../../src/sandbox/session-retirement.ts"],"sourcesContent":["import { releaseSessionSandbox } from '../integrations/github/sandbox-release.js';\nimport { DEFAULT_COMMAND_TIMEOUT_MS, runTeardownCommand } from '../integrations/github/sandbox.js';\nimport { peekSessionEnvironmentTeardown } from '../session/environment-state-processor.js';\nimport type {\n ProjectRepository,\n SourceControlSession,\n SourceControlStorageHandle,\n} from '../storage/domains/source-control/base.js';\nimport type { WorkItemsStorage } from '../storage/domains/work-items/base.js';\nimport { requireExec } from './materialization.js';\nimport { peekSessionSandbox } from './session-sandbox.js';\n\ntype WarningLogger = (message: string, details: Record<string, unknown>) => void;\n\nexport interface SessionRetirementCoordinatorOptions {\n invalidateSession?: (sessionId: string) => Promise<void> | void;\n warn?: WarningLogger;\n}\n\nexport interface RetireSessionInput {\n sourceControl: SourceControlStorageHandle;\n /** When provided, deleting the session row also strips its work-item refs. */\n workItems?: Pick<WorkItemsStorage, 'clearSessionReferences'>;\n orgId: string;\n sessionId: string;\n deleteSession: boolean;\n}\n\nfunction boundedError(error: unknown): string {\n const detail = error instanceof Error ? error.message : String(error);\n if (detail.length <= 2000) return detail;\n return `${detail.slice(0, 200)}...${detail.slice(-1797)}`;\n}\n\n/**\n * Owns terminal and destructive session cleanup. Each session is serialized so\n * duplicate role bindings and competing deletion/transition requests cannot\n * run teardown concurrently.\n *\n * Sandbox identity is the session id: retirement stops (or, for deleted\n * sessions, destroys) the sandbox this process holds in the session memo.\n * Sessions opened on another replica have no memo entry here — their VMs are\n * left to the provider's idle lifecycle (pause / idle GC), which is the\n * provider-owned half of the contract.\n */\nexport class SessionRetirementCoordinator {\n readonly #invalidateSession: NonNullable<SessionRetirementCoordinatorOptions['invalidateSession']>;\n readonly #warn: WarningLogger;\n readonly #locks = new Map<string, Promise<void>>();\n\n constructor(options: SessionRetirementCoordinatorOptions = {}) {\n this.#invalidateSession = options.invalidateSession ?? (() => {});\n this.#warn = options.warn ?? ((message, details) => console.warn(`[Mastra Factory] ${message}`, details));\n }\n\n async retireSession(input: RetireSessionInput): Promise<void> {\n const previous = this.#locks.get(input.sessionId) ?? Promise.resolve();\n const current = previous.catch(() => {}).then(() => this.#retireSession(input));\n this.#locks.set(input.sessionId, current);\n try {\n await current;\n } finally {\n if (this.#locks.get(input.sessionId) === current) this.#locks.delete(input.sessionId);\n }\n }\n\n async retireWorkItemSessions(options: {\n workItems: Pick<WorkItemsStorage, 'get'>;\n sourceControl: SourceControlStorageHandle;\n orgId: string;\n workItemId: string;\n }): Promise<void> {\n const item = await options.workItems.get({ orgId: options.orgId, id: options.workItemId });\n if (!item) return;\n const sessionIds = [...new Set(Object.values(item.sessions).map(session => session.sessionId))];\n await Promise.all(\n sessionIds.map(sessionId =>\n this.retireSession({\n sourceControl: options.sourceControl,\n orgId: options.orgId,\n sessionId,\n deleteSession: false,\n }),\n ),\n );\n }\n\n async retireProjectRepositorySessions(options: {\n sourceControl: SourceControlStorageHandle;\n workItems?: Pick<WorkItemsStorage, 'clearSessionReferences'>;\n orgId: string;\n projectRepositoryId: string;\n }): Promise<void> {\n const sessions = await options.sourceControl.sessions.listByProjectRepository({\n projectRepositoryId: options.projectRepositoryId,\n });\n await Promise.all(\n sessions.map(session =>\n this.retireSession({\n sourceControl: options.sourceControl,\n ...(options.workItems ? { workItems: options.workItems } : {}),\n orgId: options.orgId,\n sessionId: session.sessionId,\n deleteSession: true,\n }),\n ),\n );\n }\n\n async #retireSession(input: RetireSessionInput): Promise<void> {\n const session = await input.sourceControl.sessions.getBySessionId(input.sessionId);\n if (!session || session.orgId !== input.orgId) return;\n\n try {\n await this.#teardownSessionSandbox(input, session);\n } finally {\n try {\n await this.#invalidateSession(session.sessionId);\n } catch (error) {\n this.#warn('Factory session workspace cache invalidation failed', {\n orgId: session.orgId,\n sessionId: session.sessionId,\n projectRepositoryId: session.projectRepositoryId,\n error: boundedError(error),\n });\n }\n\n if (input.deleteSession) {\n // Refs first: clearing again is a no-op, but refs on a deleted row would dangle forever.\n await input.workItems?.clearSessionReferences({ orgId: input.orgId, sessionId: input.sessionId });\n await input.sourceControl.sessions.delete(session.id);\n }\n }\n }\n\n async #teardownSessionSandbox(input: RetireSessionInput, session: SourceControlSession): Promise<void> {\n const entry = peekSessionSandbox(session.id);\n if (!entry) return;\n\n // A session booted from its factory environment tears every repository\n // down in position order, each in its own directory; a failure is logged\n // and the next repository still runs.\n const environmentTeardown = peekSessionEnvironmentTeardown(session.sessionId);\n if (environmentTeardown.length > 0) {\n for (const repo of environmentTeardown) {\n try {\n await runTeardownCommand(requireExec(entry.sandbox), repo.dir, repo.command, {\n timeoutMs: DEFAULT_COMMAND_TIMEOUT_MS,\n });\n } catch (error) {\n this.#warn('Factory teardown command failed', {\n orgId: session.orgId,\n sessionId: session.sessionId,\n projectRepositoryId: session.projectRepositoryId,\n repository: repo.slug,\n error: boundedError(error),\n });\n }\n }\n await this.#releaseSandbox(input, session);\n return;\n }\n\n let projectRepository: ProjectRepository | null | undefined;\n try {\n projectRepository = session.projectRepositoryId\n ? await input.sourceControl.projectRepositories.get({ orgId: input.orgId, id: session.projectRepositoryId })\n : null;\n } catch (error) {\n this.#warn('Factory repository settings could not be loaded for session retirement', {\n orgId: session.orgId,\n sessionId: session.sessionId,\n projectRepositoryId: session.projectRepositoryId,\n error: boundedError(error),\n });\n }\n\n // An unresolved workdir means the sandbox never started in this process:\n // nothing was set up, so there is nothing for a teardown command to undo.\n if (projectRepository?.teardownCommand && entry.workdir) {\n try {\n await runTeardownCommand(requireExec(entry.sandbox), entry.workdir, projectRepository.teardownCommand, {\n timeoutMs: DEFAULT_COMMAND_TIMEOUT_MS,\n });\n } catch (error) {\n this.#warn('Factory teardown command failed', {\n orgId: session.orgId,\n sessionId: session.sessionId,\n projectRepositoryId: session.projectRepositoryId,\n error: boundedError(error),\n });\n }\n }\n\n await this.#releaseSandbox(input, session);\n }\n\n async #releaseSandbox(input: RetireSessionInput, session: SourceControlSession): Promise<void> {\n try {\n await releaseSessionSandbox({ sessionId: session.id, destroy: input.deleteSession });\n } catch (error) {\n this.#warn('Factory session sandbox release failed', {\n orgId: session.orgId,\n sessionId: session.sessionId,\n projectRepositoryId: session.projectRepositoryId,\n error: boundedError(error),\n });\n }\n }\n}\n"],"mappings":"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"}
@@ -1,43 +1,12 @@
1
- import type { MastraSandbox, SandboxStartHook, WorkspaceSandbox } from '@mastra/core/workspace';
2
- import type { RepositoryAccess } from '../capabilities/version-control.js';
1
+ import type { FactorySandbox, FactorySandboxContext as CoreFactorySandboxContext, MastraSandbox, SandboxStartHook, WorkspaceSandbox } from '@mastra/core/workspace';
3
2
  /**
4
- * Everything factory knows about a session's sandbox needs — the whole
5
- * contract between factory and the deployer's sandbox callback. Factory owns
6
- * intent; the provider owns resolving `sessionId` to a runnable VM.
3
+ * Everything factory knows about a session's sandbox needs: the whole contract
4
+ * between factory and the host's FactorySandbox. Factory owns intent; the
5
+ * provider owns resolving `sessionId` to a runnable VM. Defined in
6
+ * `@mastra/core/workspace` so provider packages can implement it without
7
+ * depending on factory.
7
8
  */
8
- export interface FactorySandboxContext {
9
- /** Stable session id — the sandbox identity. */
10
- sessionId: string;
11
- /**
12
- * The provider's physical sandbox id persisted from a prior start, when the
13
- * session has been started before. Providers that reattach by physical id
14
- * (e.g. Railway) MUST forward it to the sandbox constructor so resume
15
- * reattaches the original VM instead of provisioning a replacement. E2B and
16
- * Platform accept it as a deterministic-reattach optimization. Undefined on
17
- * a session's first ever start.
18
- */
19
- sandboxId?: string;
20
- /** owner/name of the repository, when the session is repo-backed. */
21
- repoFullName?: string;
22
- /**
23
- * Configured repo setup command, when present. Part of a repo template's
24
- * identity: a different setup command produces a different template.
25
- */
26
- setupCommand?: string;
27
- /**
28
- * Resolves the session repository's clone URL and a fresh short-lived
29
- * credential for it. Providers use it for authenticated work that runs
30
- * outside the VM — resolving a private repo's head, or cloning it during
31
- * a template build. The credential is minted per call (installation
32
- * tokens expire in ~1h); never an org PAT.
33
- *
34
- * `undefined` when the session has no repository, which is how a provider
35
- * knows to build no repo template. The key is always present so that
36
- * passing the whole context to a provider helper keeps working when this
37
- * field changes, instead of silently resolving to "no repository".
38
- */
39
- getRepositoryAccess: (() => Promise<RepositoryAccess>) | undefined;
40
- }
9
+ export type FactorySandboxContext = CoreFactorySandboxContext;
41
10
  /**
42
11
  * The deploy's sandbox configuration: construct a session's sandbox from
43
12
  * intent. The sandbox identity is the session id; the provider must honor
@@ -62,8 +31,17 @@ export interface FactorySandboxContext {
62
31
  * ```typescript
63
32
  * sandbox: ({ sessionId, sandboxId }) => new E2BSandbox({ id: sessionId, sandboxId })
64
33
  * ```
34
+ *
35
+ * A FactorySandbox instance (for example `new E2BFactorySandbox({ apiKey })`)
36
+ * additionally exposes settings and template builds to the environment.
65
37
  */
66
38
  export type MastraFactorySandboxConfig = (ctx: FactorySandboxContext) => MastraSandbox;
39
+ /**
40
+ * The `sandbox` option of `MastraFactoryConfig`: a FactorySandbox instance
41
+ * (it owns the session sandbox constructor, the repo template and the
42
+ * environment settings schema) or a bare callback, which the factory wraps as a `provider: 'custom'` sandbox with no settings.
43
+ */
44
+ export type MastraFactorySandboxOption = FactorySandbox | MastraFactorySandboxConfig;
67
45
  /**
68
46
  * What the start hook learned about the setup command before running the
69
47
  * session setup, and how to record its completion afterwards.
@@ -80,6 +58,27 @@ export interface SessionSetupGate {
80
58
  * still needs its pull); only the setup command consults `gate`.
81
59
  */
82
60
  export type SessionSetupRun = (sandbox: WorkspaceSandbox, workdir: string, gate: SessionSetupGate) => Promise<void>;
61
+ /** One environment repository as the boot hook needs it: its slug and setup command. */
62
+ export interface SessionEnvironmentRepository {
63
+ slug: string;
64
+ setupCommand?: string;
65
+ }
66
+ /** What the environment hook learned about one repository before the boot runs. */
67
+ export interface SessionEnvironmentRepositoryGate extends SessionEnvironmentRepository {
68
+ /** `<root>/<repoDir>`, the directory the template cloned this repository into. */
69
+ dir: string;
70
+ gate: SessionSetupGate;
71
+ }
72
+ /** The environment hook's view of the workspace: the root, every repository in position order, the workspace gate. */
73
+ export interface SessionEnvironmentGate {
74
+ /** The workspace root, parent of every repository directory. */
75
+ root: string;
76
+ repos: SessionEnvironmentRepositoryGate[];
77
+ /** Gate for `workspaceSetupCommand`; `setupDone` when there is no command or its marker matches. */
78
+ workspace: SessionSetupGate;
79
+ }
80
+ /** The list-form session boot: run on every start, like `SessionSetupRun`. */
81
+ export type SessionEnvironmentSetupRun = (sandbox: WorkspaceSandbox, environment: SessionEnvironmentGate) => Promise<void>;
83
82
  /**
84
83
  * Per-process session-id → sandbox instance memo.
85
84
  *
@@ -100,14 +99,38 @@ interface SessionSandboxEntry {
100
99
  * treat an unresolved workdir as "nothing materialized".
101
100
  */
102
101
  workdir?: string;
102
+ /** The session's own repository, the only one `workdir` is ever recorded for. */
103
+ ownRepo: string;
104
+ /** Environment repositories in position order; the session's own repository is among them. */
105
+ repos?: string[];
106
+ }
107
+ /** One environment repository as the file readers see it. */
108
+ export interface SessionLayoutRepository {
109
+ slug: string;
110
+ /** Absolute checkout directory. */
111
+ dir: string;
112
+ /** Path prefix of this checkout under the layout root: empty for a single-repository session, `<dir>/` otherwise. */
113
+ prefix: string;
103
114
  }
115
+ /**
116
+ * Where a session's files are read from. A session with one repository is
117
+ * rooted at that checkout, as before environments existed; a session with
118
+ * several is rooted at the workspace root with each checkout as a directory
119
+ * beneath it.
120
+ */
121
+ export interface SessionLayout {
122
+ root: string;
123
+ repos: SessionLayoutRepository[];
124
+ }
125
+ /** The layout of a memoized session, `undefined` until its workdir resolved. */
126
+ export declare function sessionLayout(entry: SessionSandboxEntry | undefined): SessionLayout | undefined;
104
127
  /**
105
128
  * Get the session's memoized sandbox entry, constructing (and memoizing) it on
106
129
  * first access. Construction is cheap and side-effect-free by contract; VMs
107
130
  * are provisioned on `start()` only. Local sandboxes get their workdir here;
108
131
  * remote workdirs are a runtime fact of the VM, resolved on first start.
109
132
  */
110
- export declare function getSessionSandbox(sessionId: string, repoFullName: string, construct: () => WorkspaceSandbox): SessionSandboxEntry;
133
+ export declare function getSessionSandbox(sessionId: string, repoFullName: string, construct: () => WorkspaceSandbox, repos?: string[]): SessionSandboxEntry;
111
134
  /**
112
135
  * Resolve (and memoize on the session entry) the session's repo checkout
113
136
  * root. Local sandboxes answer synchronously from their configured
@@ -115,7 +138,9 @@ export declare function getSessionSandbox(sessionId: string, repoFullName: strin
115
138
  * so the first resolution probes it with one `pwd` — the VM tells us where
116
139
  * home is, we never invent a path. Calling this against a stopped sandbox
117
140
  * lazily starts it (the probe is a command), so passive readers must peek
118
- * `entry.workdir` instead.
141
+ * `entry.workdir` instead. The memo is the session's own checkout; another
142
+ * environment repository lives beside it under the same root, so it is
143
+ * answered from the memo's parent without a second probe.
119
144
  */
120
145
  export declare function resolveSessionWorkdir(sessionId: string, sandbox: WorkspaceSandbox, repoFullName: string): Promise<string>;
121
146
  /**
@@ -139,5 +164,22 @@ export declare function hasFailedSetupCommand(sessionId: string, command: string
139
164
  * fails `start()` loudly; core treats onStart errors as fatal.
140
165
  */
141
166
  export declare function createSessionSetupHook(run: SessionSetupRun, sessionId: string, repoFullName: string, setupCommand: string | undefined): SandboxStartHook;
167
+ /**
168
+ * The list-form start hook for a session whose factory has an environment:
169
+ * the same lifecycle as `createSessionSetupHook`, with one gate per
170
+ * repository plus one for the workspace setup command. The markers are the
171
+ * ones the multi-repo templates write (`.mastra-sandbox/repos/<repoDir>`,
172
+ * `.mastra-sandbox/workspace-setup`, `.mastra-sandbox/setup-failed`), all
173
+ * under the workspace root, which is the parent of the primary repository's
174
+ * directory: `resolveSessionWorkdir` keeps answering that directory so the
175
+ * PR tools and passive readers see the primary checkout, and the root is
176
+ * derived from it rather than probed twice.
177
+ *
178
+ * `repos` is in position order and must include the primary (`repoFullName`).
179
+ */
180
+ export declare function createEnvironmentSetupHook(run: SessionEnvironmentSetupRun, sessionId: string, repoFullName: string, environment: {
181
+ repos: SessionEnvironmentRepository[];
182
+ workspaceSetupCommand?: string;
183
+ }): SandboxStartHook;
142
184
  export {};
143
185
  //# sourceMappingURL=session-sandbox.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"session-sandbox.d.ts","sourceRoot":"","sources":["../../src/sandbox/session-sandbox.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAChG,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAI3E;;;;GAIG;AACH,MAAM,WAAW,qBAAqB;IACpC,gDAAgD;IAChD,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qEAAqE;IACrE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;;;;;;;OAWG;IACH,mBAAmB,EAAE,CAAC,MAAM,OAAO,CAAC,gBAAgB,CAAC,CAAC,GAAG,SAAS,CAAC;CACpE;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,MAAM,0BAA0B,GAAG,CAAC,GAAG,EAAE,qBAAqB,KAAK,aAAa,CAAC;AAEvF;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,oFAAoF;IACpF,SAAS,EAAE,OAAO,CAAC;IACnB,sEAAsE;IACtE,aAAa,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACpC;AAED;;;;GAIG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,gBAAgB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAEpH;;;;;;;;;GASG;AACH,UAAU,mBAAmB;IAC3B,OAAO,EAAE,gBAAgB,CAAC;IAC1B;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAID;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,gBAAgB,GAChC,mBAAmB,CAQrB;AAED;;;;;;;;GAQG;AACH,wBAAsB,qBAAqB,CACzC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,gBAAgB,EACzB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,MAAM,CAAC,CASjB;AAmBD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,mBAAmB,GAAG,SAAS,CAErF;AAED,uFAAuF;AACvF,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAG3D;AAED,4DAA4D;AAC5D,wBAAgB,+BAA+B,IAAI,IAAI,CAGtD;AAcD,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAEjF;AAED,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAEjF;AAqCD;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,eAAe,EACpB,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,GAAG,SAAS,GAC/B,gBAAgB,CAyBlB"}
1
+ {"version":3,"file":"session-sandbox.d.ts","sourceRoot":"","sources":["../../src/sandbox/session-sandbox.ts"],"names":[],"mappings":"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"}
@@ -1,23 +1,53 @@
1
1
  import { timedPhase } from "../timing.js";
2
- import { SETUP_MARKER_PATH, setupMarkerContent } from "../packages/_internals/workspace/dist/index.js";
3
- import { deriveLocalWorkdir, deriveRemoteRepoDir, repoDirUnder } from "./workdir.js";
2
+ import { SETUP_FAILED_MARKER_PATH, SETUP_MARKER_PATH, WORKSPACE_SETUP_MARKER_PATH, repoSetupMarkerPath, setupMarkerContent } from "../packages/_internals/workspace/dist/index.js";
3
+ import { deriveLocalWorkdir, deriveRemoteRepoDir, repoDirUnder, repositoryDirectoryName } from "./workdir.js";
4
4
  import path from "path";
5
5
  //#region src/sandbox/session-sandbox.ts
6
6
  const sessionSandboxes = /* @__PURE__ */ new Map();
7
+ /** The layout of a memoized session, `undefined` until its workdir resolved. */
8
+ function sessionLayout(entry) {
9
+ if (!entry?.workdir) return void 0;
10
+ const slugs = entry.repos?.length ? entry.repos : [entry.ownRepo];
11
+ if (slugs.length === 1) return {
12
+ root: entry.workdir,
13
+ repos: [{
14
+ slug: slugs[0],
15
+ dir: entry.workdir,
16
+ prefix: ""
17
+ }]
18
+ };
19
+ const root = path.posix.dirname(entry.workdir);
20
+ return {
21
+ root,
22
+ repos: slugs.map((slug) => {
23
+ const dir = slug.toLowerCase() === entry.ownRepo.toLowerCase() ? entry.workdir : repoDirUnder(root, slug);
24
+ return {
25
+ slug,
26
+ dir,
27
+ prefix: `${path.posix.basename(dir)}/`
28
+ };
29
+ })
30
+ };
31
+ }
7
32
  /**
8
33
  * Get the session's memoized sandbox entry, constructing (and memoizing) it on
9
34
  * first access. Construction is cheap and side-effect-free by contract; VMs
10
35
  * are provisioned on `start()` only. Local sandboxes get their workdir here;
11
36
  * remote workdirs are a runtime fact of the VM, resolved on first start.
12
37
  */
13
- function getSessionSandbox(sessionId, repoFullName, construct) {
38
+ function getSessionSandbox(sessionId, repoFullName, construct, repos) {
14
39
  const existing = sessionSandboxes.get(sessionId);
15
- if (existing) return existing;
40
+ if (existing) {
41
+ if (repos) existing.repos = repos;
42
+ return existing;
43
+ }
16
44
  const sandbox = construct();
17
45
  const local = deriveLocalWorkdir(sandbox, repoFullName);
18
46
  const entry = {
19
47
  sandbox,
20
- ...local ? { workdir: local } : {}
48
+ ownRepo: repoFullName,
49
+ ...local ? { workdir: local } : {},
50
+ ...repos ? { repos } : {}
21
51
  };
22
52
  sessionSandboxes.set(sessionId, entry);
23
53
  return entry;
@@ -29,13 +59,20 @@ function getSessionSandbox(sessionId, repoFullName, construct) {
29
59
  * so the first resolution probes it with one `pwd` — the VM tells us where
30
60
  * home is, we never invent a path. Calling this against a stopped sandbox
31
61
  * lazily starts it (the probe is a command), so passive readers must peek
32
- * `entry.workdir` instead.
62
+ * `entry.workdir` instead. The memo is the session's own checkout; another
63
+ * environment repository lives beside it under the same root, so it is
64
+ * answered from the memo's parent without a second probe.
33
65
  */
34
66
  async function resolveSessionWorkdir(sessionId, sandbox, repoFullName) {
35
67
  const entry = sessionSandboxes.get(sessionId);
36
- if (entry?.workdir && entry.sandbox === sandbox) return entry.workdir;
68
+ const memo = entry && entry.sandbox === sandbox ? entry : void 0;
69
+ if (memo && memo.ownRepo !== repoFullName) {
70
+ const own = await resolveSessionWorkdir(sessionId, sandbox, memo.ownRepo);
71
+ return repoDirUnder(path.posix.dirname(own), repoFullName);
72
+ }
73
+ if (memo?.workdir) return memo.workdir;
37
74
  const workdir = deriveLocalWorkdir(sandbox, repoFullName) ?? deriveRemoteRepoDir(sandbox, repoFullName) ?? repoDirUnder(await probeHome(sandbox), repoFullName);
38
- if (entry && entry.sandbox === sandbox) entry.workdir = workdir;
75
+ if (memo) memo.workdir = workdir;
39
76
  return workdir;
40
77
  }
41
78
  /** One `pwd` in the VM's default shell cwd — its home dir, by provider convention. */
@@ -70,16 +107,18 @@ function __clearSessionSandboxesForTests() {
70
107
  * that triggered it. Recording it lets the next start skip the known-bad
71
108
  * command instead of wedging the session behind a permanently failing
72
109
  * onStart: clone and checkout still run, and the agent can fix or re-run
73
- * the setup itself. Keyed by the exact command so an edited setup command
74
- * runs fresh. In-memory only — a server restart re-runs the (idempotent)
110
+ * the setup itself. Keyed by the exact command, one entry per failed command, so an
111
+ * edited setup command runs fresh. In-memory only — a server restart re-runs the (idempotent)
75
112
  * setup.
76
113
  */
77
114
  const failedSetupCommands = /* @__PURE__ */ new Map();
78
115
  function recordFailedSetupCommand(sessionId, command) {
79
- failedSetupCommands.set(sessionId, command);
116
+ const failed = failedSetupCommands.get(sessionId) ?? /* @__PURE__ */ new Set();
117
+ failed.add(command);
118
+ failedSetupCommands.set(sessionId, failed);
80
119
  }
81
120
  function hasFailedSetupCommand(sessionId, command) {
82
- return failedSetupCommands.get(sessionId) === command;
121
+ return failedSetupCommands.get(sessionId)?.has(command) ?? false;
83
122
  }
84
123
  /**
85
124
  * The setup completion marker is a convention shared with the repo templates
@@ -125,7 +164,85 @@ function createSessionSetupHook(run, sessionId, repoFullName, setupCommand) {
125
164
  });
126
165
  };
127
166
  }
167
+ /**
168
+ * Shell-quote one marker file's content. Marker contents are digests, but
169
+ * the quoting keeps an edited command's digest from ever reaching the shell
170
+ * unquoted.
171
+ */
172
+ function shellQuote(value) {
173
+ return `'${value.replace(/'/g, `'\\''`)}'`;
174
+ }
175
+ /**
176
+ * The list-form marker probe: trust a repository's marker only when its
177
+ * checkout exists, the marker names this command, and the template did not
178
+ * record the repository in `setup-failed` (a failed per-repo setup still
179
+ * writes the marker because every step ran; the failure list says it failed).
180
+ */
181
+ async function repoMarkerMatches(sandbox, root, repoDir, content) {
182
+ const marker = `${root}/${repoSetupMarkerPath(repoDir)}`;
183
+ const failed = `${root}/${SETUP_FAILED_MARKER_PATH}`;
184
+ return (await sandbox.executeCommand(`test -d "${root}/${repoDir}/.git" && test -f "${marker}" && [ "$(cat "${marker}")" = ${shellQuote(content)} ] && ! grep -qxF -- ${shellQuote(repoDir)} "${failed}" 2>/dev/null`)).exitCode === 0;
185
+ }
186
+ /** Write a repository's marker and drop it from `setup-failed`. Best-effort, like `writeMarker`. */
187
+ async function writeRepoMarker(sandbox, root, repoDir, content) {
188
+ const marker = `${root}/${repoSetupMarkerPath(repoDir)}`;
189
+ const failed = `${root}/${SETUP_FAILED_MARKER_PATH}`;
190
+ await sandbox.executeCommand(`mkdir -p "$(dirname "${marker}")" && printf '%s' ${shellQuote(content)} > "${marker}" && { test -f "${failed}" && { grep -vxF -- ${shellQuote(repoDir)} "${failed}" > "${failed}.tmp" || true; } && mv "${failed}.tmp" "${failed}" || true; }`).catch(() => {});
191
+ }
192
+ async function workspaceMarkerMatches(sandbox, root, content) {
193
+ const marker = `${root}/${WORKSPACE_SETUP_MARKER_PATH}`;
194
+ return (await sandbox.executeCommand(`test -f "${marker}" && [ "$(cat "${marker}")" = ${shellQuote(content)} ]`)).exitCode === 0;
195
+ }
196
+ async function writeWorkspaceMarker(sandbox, root, content) {
197
+ const marker = `${root}/${WORKSPACE_SETUP_MARKER_PATH}`;
198
+ await sandbox.executeCommand(`mkdir -p "$(dirname "${marker}")" && printf '%s' ${shellQuote(content)} > "${marker}"`).catch(() => {});
199
+ }
200
+ /**
201
+ * The list-form start hook for a session whose factory has an environment:
202
+ * the same lifecycle as `createSessionSetupHook`, with one gate per
203
+ * repository plus one for the workspace setup command. The markers are the
204
+ * ones the multi-repo templates write (`.mastra-sandbox/repos/<repoDir>`,
205
+ * `.mastra-sandbox/workspace-setup`, `.mastra-sandbox/setup-failed`), all
206
+ * under the workspace root, which is the parent of the primary repository's
207
+ * directory: `resolveSessionWorkdir` keeps answering that directory so the
208
+ * PR tools and passive readers see the primary checkout, and the root is
209
+ * derived from it rather than probed twice.
210
+ *
211
+ * `repos` is in position order and must include the primary (`repoFullName`).
212
+ */
213
+ function createEnvironmentSetupHook(run, sessionId, repoFullName, environment) {
214
+ const workspaceCommand = environment.workspaceSetupCommand?.trim() ? environment.workspaceSetupCommand : void 0;
215
+ const workspaceMarker = workspaceCommand ? setupMarkerContent(workspaceCommand) : void 0;
216
+ return async ({ sandbox }) => {
217
+ if (!sandbox.executeCommand) throw new Error(`Sandbox '${sandbox.id}' cannot run the session setup: no executeCommand implementation`);
218
+ const workdir = await resolveSessionWorkdir(sessionId, sandbox, repoFullName);
219
+ const root = path.posix.dirname(workdir);
220
+ const repos = [];
221
+ for (const repo of environment.repos) {
222
+ const repoDir = repositoryDirectoryName(repo.slug);
223
+ const marker = repo.setupCommand?.trim() ? setupMarkerContent(repo.setupCommand) : void 0;
224
+ const setupDone = marker ? await timedPhase(`workspace.setup-marker(${repoDir})`, () => repoMarkerMatches(sandbox, root, repoDir, marker)) : true;
225
+ if (marker) process.stderr.write(`[factory:setup] ${repo.slug}: ${setupDone ? "marker matches, skipping setup command" : "no matching marker, setup command will run"} (${root}/${repoSetupMarkerPath(repoDir)})\n`);
226
+ repos.push({
227
+ ...repo,
228
+ dir: `${root}/${repoDir}`,
229
+ gate: {
230
+ setupDone,
231
+ markSetupDone: () => marker ? writeRepoMarker(sandbox, root, repoDir, marker) : Promise.resolve()
232
+ }
233
+ });
234
+ }
235
+ await run(sandbox, {
236
+ root,
237
+ repos,
238
+ workspace: {
239
+ setupDone: workspaceMarker ? await timedPhase("workspace.setup-marker(workspace)", () => workspaceMarkerMatches(sandbox, root, workspaceMarker)) : true,
240
+ markSetupDone: () => workspaceMarker ? writeWorkspaceMarker(sandbox, root, workspaceMarker) : Promise.resolve()
241
+ }
242
+ });
243
+ };
244
+ }
128
245
  //#endregion
129
- export { __clearSessionSandboxesForTests, createSessionSetupHook, evictSessionSandbox, getSessionSandbox, hasFailedSetupCommand, peekSessionSandbox, recordFailedSetupCommand, resolveSessionWorkdir };
246
+ export { __clearSessionSandboxesForTests, createEnvironmentSetupHook, createSessionSetupHook, evictSessionSandbox, getSessionSandbox, hasFailedSetupCommand, peekSessionSandbox, recordFailedSetupCommand, resolveSessionWorkdir, sessionLayout };
130
247
 
131
248
  //# sourceMappingURL=session-sandbox.js.map