@mastra/factory 0.21.1-alpha.3 → 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 (174) 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/session/environment-repositories.d.ts +31 -0
  129. package/dist/session/environment-repositories.d.ts.map +1 -0
  130. package/dist/session/environment-repositories.js +89 -0
  131. package/dist/session/environment-repositories.js.map +1 -0
  132. package/dist/session/environment-state-processor.d.ts +59 -0
  133. package/dist/session/environment-state-processor.d.ts.map +1 -0
  134. package/dist/session/environment-state-processor.js +105 -0
  135. package/dist/session/environment-state-processor.js.map +1 -0
  136. package/dist/session/factory-session.d.ts +26 -35
  137. package/dist/session/factory-session.d.ts.map +1 -1
  138. package/dist/session/factory-session.js +34 -71
  139. package/dist/session/factory-session.js.map +1 -1
  140. package/dist/session/filesystem-capture.d.ts +13 -0
  141. package/dist/session/filesystem-capture.d.ts.map +1 -1
  142. package/dist/session/filesystem-capture.js +73 -14
  143. package/dist/session/filesystem-capture.js.map +1 -1
  144. package/dist/session/source-control-tools.d.ts +1 -1
  145. package/dist/session/source-control-tools.d.ts.map +1 -1
  146. package/dist/session/source-control-tools.js +144 -71
  147. package/dist/session/source-control-tools.js.map +1 -1
  148. package/dist/spa-static.js +2 -2
  149. package/dist/storage/domains/audit/agent-audit.d.ts +6 -1
  150. package/dist/storage/domains/audit/agent-audit.d.ts.map +1 -1
  151. package/dist/storage/domains/audit/agent-audit.js +31 -13
  152. package/dist/storage/domains/audit/agent-audit.js.map +1 -1
  153. package/dist/storage/domains/projects/base.d.ts +115 -2
  154. package/dist/storage/domains/projects/base.d.ts.map +1 -1
  155. package/dist/storage/domains/projects/base.js +219 -12
  156. package/dist/storage/domains/projects/base.js.map +1 -1
  157. package/dist/storage/domains/source-control/base.d.ts +123 -7
  158. package/dist/storage/domains/source-control/base.d.ts.map +1 -1
  159. package/dist/storage/domains/source-control/base.js +406 -33
  160. package/dist/storage/domains/source-control/base.js.map +1 -1
  161. package/dist/storage/domains/source-control/inmemory.d.ts +19 -6
  162. package/dist/storage/domains/source-control/inmemory.d.ts.map +1 -1
  163. package/dist/storage/domains/source-control/inmemory.js +90 -2
  164. package/dist/storage/domains/source-control/inmemory.js.map +1 -1
  165. package/dist/storage/domains/work-items/base.d.ts.map +1 -1
  166. package/dist/storage/domains/work-items/base.js +12 -5
  167. package/dist/storage/domains/work-items/base.js.map +1 -1
  168. package/dist/supervisor/read-tools.js +1 -1
  169. package/dist/supervisor/write-tools.js +1 -1
  170. package/dist/workspace.d.ts +50 -7
  171. package/dist/workspace.d.ts.map +1 -1
  172. package/dist/workspace.js +344 -24
  173. package/dist/workspace.js.map +1 -1
  174. package/package.json +7 -7
@@ -1 +1 @@
1
- {"version":3,"file":"factory-session.d.ts","sourceRoot":"","sources":["../../src/session/factory-session.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"factory-session.d.ts","sourceRoot":"","sources":["../../src/session/factory-session.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,6 @@
1
- import { seedSessionOrg } from "./org-seed.js";
2
1
  import { SourceControlConnectionNotFoundError } from "../storage/domains/source-control/base.js";
2
+ import { resolveEnvironmentRepositories } from "./environment-repositories.js";
3
+ import { seedSessionOrg } from "./org-seed.js";
3
4
  //#region src/session/factory-session.ts
4
5
  var FactorySourceControlConflictError = class extends Error {};
5
6
  /**
@@ -81,82 +82,41 @@ var FactorySourceSessionResolutionError = class extends Error {
81
82
  }
82
83
  };
83
84
  /**
84
- * Resolve which repository a factory project's source-control runs act on: the
85
- * owner's connection on the project, then one of its linked repositories.
85
+ * The repository link a new session is filed under. By default the position-1
86
+ * `inEnvironment` link of the factory (decision D1); a work item that targets
87
+ * a repository names its link through `projectRepositoryId`, which must be an
88
+ * environment link of the same factory. Session start no longer picks a
89
+ * repository to work in, the sandbox holds every environment repository. The
90
+ * PR tools default to this link, and subscriptions, audit and the work item's
91
+ * repository check still read it; moving them to the per-repository
92
+ * `sessionRepositories` rows is a follow-up.
86
93
  *
87
94
  * The owner is whichever integration owns source control, matched by the
88
95
  * handle's own `integrationId` — nothing here is provider-specific.
89
96
  */
90
- async function resolveFactorySourceRepository(args) {
91
- const { sourceControl, orgId, factoryProjectId } = args;
92
- const repositorySlug = "repositorySlug" in args ? args.repositorySlug : void 0;
93
- const firstLinkedRepository = "firstLinkedRepository" in args;
94
- const candidates = (await sourceControl.connections.list({
97
+ async function resolvePrimaryEnvironmentRepository(args) {
98
+ const { sourceControl, orgId, factoryProjectId, projectRepositoryId } = args;
99
+ if (!(await sourceControl.connections.list({
95
100
  orgId,
96
101
  factoryProjectId
97
- })).filter((candidate) => candidate.integrationId === sourceControl.integrationId);
98
- if (candidates.length === 0) return {
102
+ })).some((candidate) => candidate.integrationId === sourceControl.integrationId)) return {
99
103
  found: false,
100
104
  reason: "connection"
101
105
  };
102
- for (const connection of candidates) {
103
- let resolved;
104
- try {
105
- const projectRepositories = await sourceControl.projectRepositories.list({
106
- orgId,
107
- connectionId: connection.id
108
- });
109
- resolved = (await Promise.all(projectRepositories.map(async (projectRepository) => ({
110
- projectRepository,
111
- repository: await sourceControl.repositories.get({
112
- orgId,
113
- id: projectRepository.repositoryId
114
- })
115
- })))).find((candidate) => candidate.repository && (firstLinkedRepository || candidate.repository.slug === repositorySlug));
116
- } catch (error) {
117
- if (!(error instanceof SourceControlConnectionNotFoundError)) throw error;
118
- continue;
119
- }
120
- if (!resolved?.repository) continue;
121
- return {
122
- found: true,
123
- projectRepositoryId: resolved.projectRepository.id,
124
- baseBranch: resolved.projectRepository.branch ?? resolved.repository.defaultBranch,
125
- connectedByUserId: connection.createdByUserId
126
- };
127
- }
128
- return {
106
+ const primary = (await resolveEnvironmentRepositories({
107
+ sourceControl,
108
+ orgId,
109
+ factoryProjectId
110
+ })).find((candidate) => projectRepositoryId === void 0 || candidate.link.id === projectRepositoryId);
111
+ if (!primary) return {
129
112
  found: false,
130
113
  reason: "repository"
131
114
  };
132
- }
133
- /**
134
- * Walk a Factory user-session id back to the project it belongs to.
135
- *
136
- * Repo-backed channel threads are keyed by their Factory session id, which is
137
- * the only handle a session-start hook gets. This turns that id back into the
138
- * project whose configuration the session should adopt. Durable by
139
- * construction — it reads the same rows the session was created from, so it
140
- * survives restarts without any in-memory mapping.
141
- */
142
- async function resolveFactoryProjectForSession(args) {
143
- const { sourceControl, sessionId } = args;
144
- const session = await sourceControl.sessions.getBySessionId(sessionId);
145
- if (!session) return null;
146
- const projectRepository = await sourceControl.projectRepositories.get({
147
- orgId: session.orgId,
148
- id: session.projectRepositoryId
149
- });
150
- if (!projectRepository) return null;
151
- const connection = await sourceControl.connections.get({
152
- orgId: session.orgId,
153
- id: projectRepository.connectionId
154
- });
155
- if (!connection) return null;
156
115
  return {
157
- factoryProjectId: connection.factoryProjectId,
158
- orgId: session.orgId,
159
- userId: session.userId
116
+ found: true,
117
+ projectRepositoryId: primary.link.id,
118
+ baseBranch: primary.link.branch ?? primary.repository.defaultBranch,
119
+ connectedByUserId: primary.connection.createdByUserId
160
120
  };
161
121
  }
162
122
  /**
@@ -165,9 +125,11 @@ async function resolveFactoryProjectForSession(args) {
165
125
  * `FactoryStartCoordinator.prepare` requires this record to already exist —
166
126
  * `resolveSourceSession` throws `Factory session not found` otherwise — so every
167
127
  * autonomous entry point has to produce one before it can start a run. This is
168
- * that step, in one place: the owner's connection on the factory project, one of
169
- * its linked repositories, and a session on the requested branch with the
170
- * repository's pinned or default branch as the base.
128
+ * that step, in one place: the owner's connection on the factory project, the
129
+ * work item's repository link (else the position-1 environment link, D1) as
130
+ * the session's link, and a session on the requested branch with that
131
+ * repository's pinned or default branch as the base. The sandbox itself boots
132
+ * every environment repository.
171
133
  *
172
134
  * The run is attributed to `attributeToUserId` when the caller has an
173
135
  * interactive user (e.g. the approver of a proposed run), and otherwise falls
@@ -175,18 +137,19 @@ async function resolveFactoryProjectForSession(args) {
175
137
  * because a genuinely autonomous run has no interactive user of its own.
176
138
  */
177
139
  async function ensureFactorySourceSession(args) {
178
- const { sourceControl, orgId, factoryProjectId, branch, repositorySlug } = args;
179
- const resolved = await resolveFactorySourceRepository({
140
+ const { sourceControl, orgId, factoryProjectId, branch, projectRepositoryId } = args;
141
+ const resolved = await resolvePrimaryEnvironmentRepository({
180
142
  sourceControl,
181
143
  orgId,
182
144
  factoryProjectId,
183
- repositorySlug
145
+ projectRepositoryId
184
146
  });
185
147
  if (!resolved.found) throw new FactorySourceSessionResolutionError(resolved.reason);
186
148
  const userId = args.attributeToUserId ?? resolved.connectedByUserId;
187
149
  const session = await sourceControl.sessions.create({
188
150
  sessionId: globalThis.crypto.randomUUID(),
189
151
  projectRepositoryId: resolved.projectRepositoryId,
152
+ factoryProjectId,
190
153
  orgId,
191
154
  userId,
192
155
  branch,
@@ -239,6 +202,6 @@ async function hydrateFactorySession(session, args) {
239
202
  }
240
203
  }
241
204
  //#endregion
242
- export { FactorySourceControlConflictError, FactorySourceSessionResolutionError, createSourceControlSessionLookup, ensureFactorySourceSession, hydrateFactorySession, resolveFactoryDefaultModelId, resolveFactoryProjectForSession, resolveFactorySourceControl, resolveFactorySourceRepository };
205
+ export { FactorySourceControlConflictError, FactorySourceSessionResolutionError, createSourceControlSessionLookup, ensureFactorySourceSession, hydrateFactorySession, resolveFactoryDefaultModelId, resolveFactorySourceControl, resolvePrimaryEnvironmentRepository };
243
206
 
244
207
  //# sourceMappingURL=factory-session.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"factory-session.js","names":[],"sources":["../../src/session/factory-session.ts"],"sourcesContent":["import type { MastraCodeState } from '@mastra/code-sdk/schema';\nimport type { AgentController } from '@mastra/core/agent-controller';\n\nimport type { FactoryProjectsStorage } from '../storage/domains/projects/base.js';\nimport {\n SourceControlConnectionNotFoundError,\n type SourceControlSession,\n type SourceControlStorageHandle,\n} from '../storage/domains/source-control/base.js';\nimport { seedSessionOrg } from './org-seed.js';\n\ntype FactorySession = Awaited<ReturnType<AgentController<MastraCodeState>['createSession']>>;\n\nexport class FactorySourceControlConflictError extends Error {}\n\n/**\n * Read the factory project's default model. Best-effort: a missing project or an\n * uninitialized storage domain means \"no default\", never a failed run.\n */\nexport async function resolveFactoryDefaultModelId(\n projects: FactoryProjectsStorage | undefined,\n factoryProjectId: string | undefined,\n): Promise<string | undefined> {\n if (!projects || !factoryProjectId) return undefined;\n try {\n const project = await projects.getById({ id: factoryProjectId });\n return project?.defaultModelId ?? undefined;\n } catch {\n return undefined;\n }\n}\n\nexport interface SourceControlSessionLookup {\n getBySessionId(sessionId: string): Promise<SourceControlSession | null>;\n getSourceControlBySessionId(sessionId: string): Promise<SourceControlStorageHandle | null>;\n rename(args: { sessionId: string; title: string }): Promise<void>;\n markFirstMessage(args: { sessionId: string }): Promise<void>;\n markFirstMeaningfulExec(args: { sessionId: string }): Promise<void>;\n}\n\n/**\n * Read or update a session across every registered source-control partition.\n * Session ids are globally generated, so more than one match is invalid and\n * fails closed instead of mutating an arbitrary provider.\n */\nexport function createSourceControlSessionLookup(\n sourceControls: readonly SourceControlStorageHandle[],\n): SourceControlSessionLookup {\n const resolve = async (\n sessionId: string,\n ): Promise<{ sourceControl: SourceControlStorageHandle; session: SourceControlSession } | null> => {\n const matches = (\n await Promise.all(\n sourceControls.map(async sourceControl => ({\n sourceControl,\n session: await sourceControl.sessions.getBySessionId(sessionId),\n })),\n )\n ).filter(\n (match): match is { sourceControl: SourceControlStorageHandle; session: SourceControlSession } =>\n match.session !== null,\n );\n if (matches.length > 1) throw new Error('Factory session exists in multiple source-control providers.');\n return matches[0] ?? null;\n };\n\n return {\n getBySessionId: async sessionId => (await resolve(sessionId))?.session ?? null,\n getSourceControlBySessionId: async sessionId => (await resolve(sessionId))?.sourceControl ?? null,\n rename: async args => (await resolve(args.sessionId))?.sourceControl.sessions.rename(args),\n markFirstMessage: async args => (await resolve(args.sessionId))?.sourceControl.sessions.markFirstMessage(args),\n markFirstMeaningfulExec: async args =>\n (await resolve(args.sessionId))?.sourceControl.sessions.markFirstMeaningfulExec(args),\n };\n}\n\n/**\n * Resolve the source-control provider from durable Factory repository links,\n * never from the issue tracker that happened to create the work item.\n */\nexport async function resolveFactorySourceControl(args: {\n sourceControls: readonly SourceControlStorageHandle[];\n orgId: string;\n factoryProjectId: string;\n sessionId?: string;\n}): Promise<SourceControlStorageHandle | undefined> {\n if (args.sessionId) {\n const sessionMatches = (\n await Promise.all(\n args.sourceControls.map(async sourceControl => ({\n sourceControl,\n session: await sourceControl.sessions.getBySessionId(args.sessionId!),\n })),\n )\n ).filter(match => match.session !== null);\n if (sessionMatches.length > 1) throw new Error('Factory session exists in multiple source-control providers.');\n if (sessionMatches[0]) return sessionMatches[0].sourceControl;\n }\n\n const linked = [];\n for (const sourceControl of args.sourceControls) {\n const connections = await sourceControl.connections.list({\n orgId: args.orgId,\n factoryProjectId: args.factoryProjectId,\n });\n let hasLinkedRepository = false;\n for (const connection of connections) {\n try {\n if (\n (await sourceControl.projectRepositories.list({ orgId: args.orgId, connectionId: connection.id })).length > 0\n ) {\n hasLinkedRepository = true;\n break;\n }\n } catch (error) {\n if (!(error instanceof SourceControlConnectionNotFoundError)) throw error;\n }\n }\n if (hasLinkedRepository) linked.push(sourceControl);\n }\n if (linked.length > 1)\n throw new FactorySourceControlConflictError(\n 'Factory project has repositories linked through multiple source-control providers.',\n );\n return linked[0];\n}\n\nexport interface EnsureFactorySourceSessionArgs {\n /**\n * Storage handle of the integration that owns source control. Nothing here is\n * provider-specific: the connection is matched by the handle's own\n * `integrationId`, so GitHub, Slack-on-behalf-of-GitHub, or any future owner\n * all resolve through the same traversal.\n */\n sourceControl: SourceControlStorageHandle;\n orgId: string;\n factoryProjectId: string;\n branch: string;\n /** Pick the linked repository that the work item targets. */\n repositorySlug: string;\n /**\n * Attribute the run to this user instead of the repo connector. Set when the\n * run has an interactive user — e.g. the person who approved a proposed run.\n */\n attributeToUserId?: string;\n}\n\nexport interface EnsuredFactorySourceSession {\n sessionId: string;\n userId: string;\n projectRepositoryId: string;\n branch: string;\n baseBranch: string;\n}\n\nexport class FactorySourceSessionResolutionError extends Error {\n constructor(readonly reason: 'connection' | 'repository') {\n super(\n reason === 'connection'\n ? 'Factory source-control connection not found.'\n : 'Factory source-control repository not found.',\n );\n this.name = 'FactorySourceSessionResolutionError';\n }\n}\n\nexport interface ResolvedFactorySourceRepository {\n projectRepositoryId: string;\n /** The repository's pinned branch, else its default branch. */\n baseBranch: string;\n /** Who connected the repository. The attribution for runs with no interactive user. */\n connectedByUserId: string;\n}\n\n/**\n * Outcome of {@link resolveFactorySourceRepository}. A miss carries which step\n * failed: callers differ on whether that is an error (an autonomous run cannot\n * proceed) or a routine fallback (a chat integration drops to a chat-only\n * session), and the two steps fail for different reasons worth reporting apart.\n */\nexport type FactorySourceRepositoryResult =\n | ({ found: true } & ResolvedFactorySourceRepository)\n | { found: false; reason: 'connection' | 'repository' };\n\n/**\n * Resolve which repository a factory project's source-control runs act on: the\n * owner's connection on the project, then one of its linked repositories.\n *\n * The owner is whichever integration owns source control, matched by the\n * handle's own `integrationId` — nothing here is provider-specific.\n */\nexport async function resolveFactorySourceRepository(\n args:\n | {\n sourceControl: SourceControlStorageHandle;\n orgId: string;\n factoryProjectId: string;\n repositorySlug: string;\n }\n | {\n sourceControl: SourceControlStorageHandle;\n orgId: string;\n factoryProjectId: string;\n firstLinkedRepository: true;\n },\n): Promise<FactorySourceRepositoryResult> {\n const { sourceControl, orgId, factoryProjectId } = args;\n const repositorySlug = 'repositorySlug' in args ? args.repositorySlug : undefined;\n const firstLinkedRepository = 'firstLinkedRepository' in args;\n\n const connections = await sourceControl.connections.list({ orgId, factoryProjectId });\n const candidates = connections.filter(candidate => candidate.integrationId === sourceControl.integrationId);\n if (candidates.length === 0) return { found: false, reason: 'connection' };\n\n // A project can carry stale connections: a provider-app reinstall leaves the\n // old connection pointing at an installation that no longer exists, and that\n // row can sit ahead of the healthy one. Try every candidate and skip the ones\n // that no longer resolve rather than failing on the first.\n for (const connection of candidates) {\n let resolved;\n try {\n const projectRepositories = await sourceControl.projectRepositories.list({ orgId, connectionId: connection.id });\n const resolvedRepositories = await Promise.all(\n projectRepositories.map(async projectRepository => ({\n projectRepository,\n repository: await sourceControl.repositories.get({ orgId, id: projectRepository.repositoryId }),\n })),\n );\n resolved = resolvedRepositories.find(\n candidate => candidate.repository && (firstLinkedRepository || candidate.repository.slug === repositorySlug),\n );\n } catch (error) {\n // A deleted installation invalidates a stale connection; storage failures must propagate.\n if (!(error instanceof SourceControlConnectionNotFoundError)) throw error;\n continue;\n }\n if (!resolved?.repository) continue;\n\n return {\n found: true,\n projectRepositoryId: resolved.projectRepository.id,\n baseBranch: resolved.projectRepository.branch ?? resolved.repository.defaultBranch,\n connectedByUserId: connection.createdByUserId,\n };\n }\n\n return { found: false, reason: 'repository' };\n}\n\n/**\n * Walk a Factory user-session id back to the project it belongs to.\n *\n * Repo-backed channel threads are keyed by their Factory session id, which is\n * the only handle a session-start hook gets. This turns that id back into the\n * project whose configuration the session should adopt. Durable by\n * construction — it reads the same rows the session was created from, so it\n * survives restarts without any in-memory mapping.\n */\nexport async function resolveFactoryProjectForSession(args: {\n sourceControl: SourceControlStorageHandle;\n sessionId: string;\n}): Promise<{ factoryProjectId: string; orgId: string; userId: string } | null> {\n const { sourceControl, sessionId } = args;\n\n const session = await sourceControl.sessions.getBySessionId(sessionId);\n if (!session) return null;\n const projectRepository = await sourceControl.projectRepositories.get({\n orgId: session.orgId,\n id: session.projectRepositoryId,\n });\n if (!projectRepository) return null;\n const connection = await sourceControl.connections.get({ orgId: session.orgId, id: projectRepository.connectionId });\n if (!connection) return null;\n\n return { factoryProjectId: connection.factoryProjectId, orgId: session.orgId, userId: session.userId };\n}\n\n/**\n * Create the source-control session a repo-backed factory run needs.\n *\n * `FactoryStartCoordinator.prepare` requires this record to already exist —\n * `resolveSourceSession` throws `Factory session not found` otherwise — so every\n * autonomous entry point has to produce one before it can start a run. This is\n * that step, in one place: the owner's connection on the factory project, one of\n * its linked repositories, and a session on the requested branch with the\n * repository's pinned or default branch as the base.\n *\n * The run is attributed to `attributeToUserId` when the caller has an\n * interactive user (e.g. the approver of a proposed run), and otherwise falls\n * back to whoever connected the repository (`connection.createdByUserId`),\n * because a genuinely autonomous run has no interactive user of its own.\n */\nexport async function ensureFactorySourceSession(\n args: EnsureFactorySourceSessionArgs,\n): Promise<EnsuredFactorySourceSession> {\n const { sourceControl, orgId, factoryProjectId, branch, repositorySlug } = args;\n\n const resolved = await resolveFactorySourceRepository({ sourceControl, orgId, factoryProjectId, repositorySlug });\n if (!resolved.found) throw new FactorySourceSessionResolutionError(resolved.reason);\n\n const userId = args.attributeToUserId ?? resolved.connectedByUserId;\n const session = await sourceControl.sessions.create({\n sessionId: globalThis.crypto.randomUUID(),\n projectRepositoryId: resolved.projectRepositoryId,\n orgId,\n userId,\n branch,\n baseBranch: resolved.baseBranch,\n visibility: 'org',\n });\n return {\n sessionId: session.sessionId,\n userId,\n projectRepositoryId: resolved.projectRepositoryId,\n branch: session.branch,\n baseBranch: resolved.baseBranch,\n };\n}\n\nexport interface HydrateFactorySessionArgs {\n orgId: string;\n /** The factory project's default model. Without it the session keeps the SDK's built-in mode default. */\n defaultModelId?: string;\n}\n\n/**\n * Apply a factory project's main model to a session on every run.\n *\n * The model switch is best-effort. A retired model id must not sink a run that\n * is otherwise ready — the session simply keeps the default it was created with,\n * and the reason is logged. Observational-memory settings are resolved from the\n * authoritative row per invocation before memory processors run.\n */\nexport async function hydrateFactorySession(session: FactorySession, args: HydrateFactorySessionArgs): Promise<void> {\n // The org rung knowledge curation scopes on. Seeded first so it lands even if\n // a later best-effort step fails; an empty org marks the session unresolved.\n await seedSessionOrg(session, args.orgId);\n\n if (args.defaultModelId) {\n try {\n await session.model.switch(args.defaultModelId);\n } catch (error) {\n console.warn('[Factory Start] Failed to apply factory default model', {\n modelId: args.defaultModelId,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n // Subagents otherwise keep the server-wide settings (or the SDK's built-in\n // default), which may name a provider this factory has no credentials for.\n // Each role is independent, so one failure doesn't strand the others.\n for (const agentType of ['explore', 'plan', 'execute']) {\n try {\n await session.subagents.model.set({ modelId: args.defaultModelId, agentType });\n } catch (error) {\n console.warn('[Factory Start] Failed to apply factory default subagent model', {\n agentType,\n modelId: args.defaultModelId,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"factory-session.js","names":[],"sources":["../../src/session/factory-session.ts"],"sourcesContent":["import type { MastraCodeState } from '@mastra/code-sdk/schema';\nimport type { AgentController } from '@mastra/core/agent-controller';\n\nimport type { FactoryProjectsStorage } from '../storage/domains/projects/base.js';\nimport {\n SourceControlConnectionNotFoundError,\n type SourceControlSession,\n type SourceControlStorageHandle,\n} from '../storage/domains/source-control/base.js';\nimport { resolveEnvironmentRepositories } from './environment-repositories.js';\nimport { seedSessionOrg } from './org-seed.js';\n\ntype FactorySession = Awaited<ReturnType<AgentController<MastraCodeState>['createSession']>>;\n\nexport class FactorySourceControlConflictError extends Error {}\n\n/**\n * Read the factory project's default model. Best-effort: a missing project or an\n * uninitialized storage domain means \"no default\", never a failed run.\n */\nexport async function resolveFactoryDefaultModelId(\n projects: FactoryProjectsStorage | undefined,\n factoryProjectId: string | undefined,\n): Promise<string | undefined> {\n if (!projects || !factoryProjectId) return undefined;\n try {\n const project = await projects.getById({ id: factoryProjectId });\n return project?.defaultModelId ?? undefined;\n } catch {\n return undefined;\n }\n}\n\nexport interface SourceControlSessionLookup {\n getBySessionId(sessionId: string): Promise<SourceControlSession | null>;\n getSourceControlBySessionId(sessionId: string): Promise<SourceControlStorageHandle | null>;\n rename(args: { sessionId: string; title: string }): Promise<void>;\n markFirstMessage(args: { sessionId: string }): Promise<void>;\n markFirstMeaningfulExec(args: { sessionId: string }): Promise<void>;\n}\n\n/**\n * Read or update a session across every registered source-control partition.\n * Session ids are globally generated, so more than one match is invalid and\n * fails closed instead of mutating an arbitrary provider.\n */\nexport function createSourceControlSessionLookup(\n sourceControls: readonly SourceControlStorageHandle[],\n): SourceControlSessionLookup {\n const resolve = async (\n sessionId: string,\n ): Promise<{ sourceControl: SourceControlStorageHandle; session: SourceControlSession } | null> => {\n const matches = (\n await Promise.all(\n sourceControls.map(async sourceControl => ({\n sourceControl,\n session: await sourceControl.sessions.getBySessionId(sessionId),\n })),\n )\n ).filter(\n (match): match is { sourceControl: SourceControlStorageHandle; session: SourceControlSession } =>\n match.session !== null,\n );\n if (matches.length > 1) throw new Error('Factory session exists in multiple source-control providers.');\n return matches[0] ?? null;\n };\n\n return {\n getBySessionId: async sessionId => (await resolve(sessionId))?.session ?? null,\n getSourceControlBySessionId: async sessionId => (await resolve(sessionId))?.sourceControl ?? null,\n rename: async args => (await resolve(args.sessionId))?.sourceControl.sessions.rename(args),\n markFirstMessage: async args => (await resolve(args.sessionId))?.sourceControl.sessions.markFirstMessage(args),\n markFirstMeaningfulExec: async args =>\n (await resolve(args.sessionId))?.sourceControl.sessions.markFirstMeaningfulExec(args),\n };\n}\n\n/**\n * Resolve the source-control provider from durable Factory repository links,\n * never from the issue tracker that happened to create the work item.\n */\nexport async function resolveFactorySourceControl(args: {\n sourceControls: readonly SourceControlStorageHandle[];\n orgId: string;\n factoryProjectId: string;\n sessionId?: string;\n}): Promise<SourceControlStorageHandle | undefined> {\n if (args.sessionId) {\n const sessionMatches = (\n await Promise.all(\n args.sourceControls.map(async sourceControl => ({\n sourceControl,\n session: await sourceControl.sessions.getBySessionId(args.sessionId!),\n })),\n )\n ).filter(match => match.session !== null);\n if (sessionMatches.length > 1) throw new Error('Factory session exists in multiple source-control providers.');\n if (sessionMatches[0]) return sessionMatches[0].sourceControl;\n }\n\n const linked = [];\n for (const sourceControl of args.sourceControls) {\n const connections = await sourceControl.connections.list({\n orgId: args.orgId,\n factoryProjectId: args.factoryProjectId,\n });\n let hasLinkedRepository = false;\n for (const connection of connections) {\n try {\n if (\n (await sourceControl.projectRepositories.list({ orgId: args.orgId, connectionId: connection.id })).length > 0\n ) {\n hasLinkedRepository = true;\n break;\n }\n } catch (error) {\n if (!(error instanceof SourceControlConnectionNotFoundError)) throw error;\n }\n }\n if (hasLinkedRepository) linked.push(sourceControl);\n }\n if (linked.length > 1)\n throw new FactorySourceControlConflictError(\n 'Factory project has repositories linked through multiple source-control providers.',\n );\n return linked[0];\n}\n\nexport interface EnsureFactorySourceSessionArgs {\n /**\n * Storage handle of the integration that owns source control. Nothing here is\n * provider-specific: the connection is matched by the handle's own\n * `integrationId`, so GitHub, Slack-on-behalf-of-GitHub, or any future owner\n * all resolve through the same traversal.\n */\n sourceControl: SourceControlStorageHandle;\n orgId: string;\n factoryProjectId: string;\n branch: string;\n /**\n * The repository link the session is filed under: the one a work item\n * targets. Defaults to the factory's position-1 environment link (D1), for\n * callers where nothing names a repository (chat threads, user sessions).\n */\n projectRepositoryId?: string;\n /**\n * Attribute the run to this user instead of the repo connector. Set when the\n * run has an interactive user — e.g. the person who approved a proposed run.\n */\n attributeToUserId?: string;\n}\n\nexport interface EnsuredFactorySourceSession {\n sessionId: string;\n userId: string;\n projectRepositoryId: string;\n branch: string;\n baseBranch: string;\n}\n\nexport class FactorySourceSessionResolutionError extends Error {\n constructor(readonly reason: 'connection' | 'repository') {\n super(\n reason === 'connection'\n ? 'Factory source-control connection not found.'\n : 'Factory source-control repository not found.',\n );\n this.name = 'FactorySourceSessionResolutionError';\n }\n}\n\nexport interface ResolvedFactorySourceRepository {\n projectRepositoryId: string;\n /** The repository's pinned branch, else its default branch. */\n baseBranch: string;\n /** Who connected the repository. The attribution for runs with no interactive user. */\n connectedByUserId: string;\n}\n\n/**\n * Outcome of {@link resolvePrimaryEnvironmentRepository}. A miss carries which\n * step failed: callers differ on whether that is an error (an autonomous run\n * cannot proceed) or a routine fallback (a chat integration drops to a\n * chat-only session), and the two steps fail for different reasons worth\n * reporting apart.\n */\nexport type FactorySourceRepositoryResult =\n | ({ found: true } & ResolvedFactorySourceRepository)\n | { found: false; reason: 'connection' | 'repository' };\n\n/**\n * The repository link a new session is filed under. By default the position-1\n * `inEnvironment` link of the factory (decision D1); a work item that targets\n * a repository names its link through `projectRepositoryId`, which must be an\n * environment link of the same factory. Session start no longer picks a\n * repository to work in, the sandbox holds every environment repository. The\n * PR tools default to this link, and subscriptions, audit and the work item's\n * repository check still read it; moving them to the per-repository\n * `sessionRepositories` rows is a follow-up.\n *\n * The owner is whichever integration owns source control, matched by the\n * handle's own `integrationId` — nothing here is provider-specific.\n */\nexport async function resolvePrimaryEnvironmentRepository(args: {\n sourceControl: SourceControlStorageHandle;\n orgId: string;\n factoryProjectId: string;\n projectRepositoryId?: string;\n}): Promise<FactorySourceRepositoryResult> {\n const { sourceControl, orgId, factoryProjectId, projectRepositoryId } = args;\n const connections = await sourceControl.connections.list({ orgId, factoryProjectId });\n if (!connections.some(candidate => candidate.integrationId === sourceControl.integrationId)) {\n return { found: false, reason: 'connection' };\n }\n const repositories = await resolveEnvironmentRepositories({ sourceControl, orgId, factoryProjectId });\n const primary = repositories.find(\n candidate => projectRepositoryId === undefined || candidate.link.id === projectRepositoryId,\n );\n if (!primary) return { found: false, reason: 'repository' };\n return {\n found: true,\n projectRepositoryId: primary.link.id,\n baseBranch: primary.link.branch ?? primary.repository.defaultBranch,\n connectedByUserId: primary.connection.createdByUserId,\n };\n}\n\n/**\n * Create the source-control session a repo-backed factory run needs.\n *\n * `FactoryStartCoordinator.prepare` requires this record to already exist —\n * `resolveSourceSession` throws `Factory session not found` otherwise — so every\n * autonomous entry point has to produce one before it can start a run. This is\n * that step, in one place: the owner's connection on the factory project, the\n * work item's repository link (else the position-1 environment link, D1) as\n * the session's link, and a session on the requested branch with that\n * repository's pinned or default branch as the base. The sandbox itself boots\n * every environment repository.\n *\n * The run is attributed to `attributeToUserId` when the caller has an\n * interactive user (e.g. the approver of a proposed run), and otherwise falls\n * back to whoever connected the repository (`connection.createdByUserId`),\n * because a genuinely autonomous run has no interactive user of its own.\n */\nexport async function ensureFactorySourceSession(\n args: EnsureFactorySourceSessionArgs,\n): Promise<EnsuredFactorySourceSession> {\n const { sourceControl, orgId, factoryProjectId, branch, projectRepositoryId } = args;\n\n const resolved = await resolvePrimaryEnvironmentRepository({\n sourceControl,\n orgId,\n factoryProjectId,\n projectRepositoryId,\n });\n if (!resolved.found) throw new FactorySourceSessionResolutionError(resolved.reason);\n\n const userId = args.attributeToUserId ?? resolved.connectedByUserId;\n const session = await sourceControl.sessions.create({\n sessionId: globalThis.crypto.randomUUID(),\n projectRepositoryId: resolved.projectRepositoryId,\n factoryProjectId,\n orgId,\n userId,\n branch,\n baseBranch: resolved.baseBranch,\n visibility: 'org',\n });\n return {\n sessionId: session.sessionId,\n userId,\n projectRepositoryId: resolved.projectRepositoryId,\n branch: session.branch,\n baseBranch: resolved.baseBranch,\n };\n}\n\nexport interface HydrateFactorySessionArgs {\n orgId: string;\n /** The factory project's default model. Without it the session keeps the SDK's built-in mode default. */\n defaultModelId?: string;\n}\n\n/**\n * Apply a factory project's main model to a session on every run.\n *\n * The model switch is best-effort. A retired model id must not sink a run that\n * is otherwise ready — the session simply keeps the default it was created with,\n * and the reason is logged. Observational-memory settings are resolved from the\n * authoritative row per invocation before memory processors run.\n */\nexport async function hydrateFactorySession(session: FactorySession, args: HydrateFactorySessionArgs): Promise<void> {\n // The org rung knowledge curation scopes on. Seeded first so it lands even if\n // a later best-effort step fails; an empty org marks the session unresolved.\n await seedSessionOrg(session, args.orgId);\n\n if (args.defaultModelId) {\n try {\n await session.model.switch(args.defaultModelId);\n } catch (error) {\n console.warn('[Factory Start] Failed to apply factory default model', {\n modelId: args.defaultModelId,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n // Subagents otherwise keep the server-wide settings (or the SDK's built-in\n // default), which may name a provider this factory has no credentials for.\n // Each role is independent, so one failure doesn't strand the others.\n for (const agentType of ['explore', 'plan', 'execute']) {\n try {\n await session.subagents.model.set({ modelId: args.defaultModelId, agentType });\n } catch (error) {\n console.warn('[Factory Start] Failed to apply factory default subagent model', {\n agentType,\n modelId: args.defaultModelId,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n }\n}\n"],"mappings":"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"}
@@ -2,6 +2,19 @@ import type { AgentControllerEvent, SessionBeforeAgentEndListener } from '@mastr
2
2
  import type { WorkspaceSandbox } from '@mastra/core/workspace';
3
3
  import type { FilesystemFile, FilesystemStorage } from '../storage/domains/filesystem/base.js';
4
4
  import type { SourceControlStorageHandle } from '../storage/domains/source-control/base.js';
5
+ /**
6
+ * Regular files directly under the workspace root written since the session
7
+ * was created ($2 is a UTC `touch -t` stamp, `YYYYMMDDhhmm.SS`), such as the
8
+ * agent's notes. Files the template shipped with predate the session and stay
9
+ * out; the checkouts are covered by git. Dotfiles are never listed: a listed
10
+ * path becomes readable through the thread's file route, and the root is where
11
+ * setup leaves `.env`, `.pgpass`, `.netrc` and other credential files.
12
+ * `touch -t` plus `-newer` works on GNU and BSD find alike; `-newermt
13
+ * "@epoch"` is GNU only.
14
+ */
15
+ export declare const ROOT_FILES_LIST_COMMAND: string;
16
+ /** `touch -t` stamp of a moment, in UTC. */
17
+ export declare function touchStamp(date: Date): string;
5
18
  export interface FilesystemCaptureSession {
6
19
  readonly identity: {
7
20
  getResourceId(): string;
@@ -1 +1 @@
1
- {"version":3,"file":"filesystem-capture.d.ts","sourceRoot":"","sources":["../../src/session/filesystem-capture.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,6BAA6B,EAAE,MAAM,+BAA+B,CAAC;AACzG,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAG/D,OAAO,KAAK,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAC;AAC/F,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,2CAA2C,CAAC;AAgB5F,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,QAAQ,EAAE;QAAE,aAAa,IAAI,MAAM,CAAA;KAAE,CAAC;IAC/C,QAAQ,CAAC,MAAM,EAAE;QAAE,SAAS,IAAI,MAAM,CAAA;KAAE,CAAC;IACzC,YAAY,IAAI;QAAE,OAAO,CAAC,EAAE,IAAI,CAAC,gBAAgB,EAAE,gBAAgB,CAAC,CAAA;KAAE,GAAG,SAAS,CAAC;IACnF,gBAAgB,CAAC,QAAQ,EAAE,6BAA6B,GAAG,MAAM,IAAI,CAAC;IACtE,SAAS,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;CACxE;AAED,MAAM,WAAW,6BAA6B;IAC5C,UAAU,EAAE,IAAI,CAAC,iBAAiB,EAAE,cAAc,CAAC,CAAC;IACpD,aAAa,EAAE;QACb,QAAQ,EAAE,IAAI,CAAC,0BAA0B,CAAC,UAAU,CAAC,EAAE,gBAAgB,CAAC,CAAC;KAC1E,CAAC;CACH;AAED,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,EAAE,CAS5E;AAED,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,wBAAwB,EACjC,EAAE,UAAU,EAAE,aAAa,EAAE,EAAE,6BAA6B,EAC5D,GAAG,CAAC,EAAE;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAC7C,OAAO,CAAC,IAAI,CAAC,CA+Df;AAYD;;;;;GAKG;AACH,wBAAsB,+BAA+B,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAsB3G;AAED,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,wBAAwB,EACjC,YAAY,EAAE,6BAA6B,GAC1C,MAAM,IAAI,CA2FZ"}
1
+ {"version":3,"file":"filesystem-capture.d.ts","sourceRoot":"","sources":["../../src/session/filesystem-capture.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,6BAA6B,EAAE,MAAM,+BAA+B,CAAC;AACzG,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAI/D,OAAO,KAAK,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAC;AAC/F,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,2CAA2C,CAAC;AAmB5F;;;;;;;;;GASG;AACH,eAAO,MAAM,uBAAuB,QAIzB,CAAC;AAEZ,4CAA4C;AAC5C,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAM7C;AAED,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,QAAQ,EAAE;QAAE,aAAa,IAAI,MAAM,CAAA;KAAE,CAAC;IAC/C,QAAQ,CAAC,MAAM,EAAE;QAAE,SAAS,IAAI,MAAM,CAAA;KAAE,CAAC;IACzC,YAAY,IAAI;QAAE,OAAO,CAAC,EAAE,IAAI,CAAC,gBAAgB,EAAE,gBAAgB,CAAC,CAAA;KAAE,GAAG,SAAS,CAAC;IACnF,gBAAgB,CAAC,QAAQ,EAAE,6BAA6B,GAAG,MAAM,IAAI,CAAC;IACtE,SAAS,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;CACxE;AAED,MAAM,WAAW,6BAA6B;IAC5C,UAAU,EAAE,IAAI,CAAC,iBAAiB,EAAE,cAAc,CAAC,CAAC;IACpD,aAAa,EAAE;QACb,QAAQ,EAAE,IAAI,CAAC,0BAA0B,CAAC,UAAU,CAAC,EAAE,gBAAgB,CAAC,CAAC;KAC1E,CAAC;CACH;AAED,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,EAAE,CAS5E;AAED,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,wBAAwB,EACjC,EAAE,UAAU,EAAE,aAAa,EAAE,EAAE,6BAA6B,EAC5D,GAAG,CAAC,EAAE;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAC7C,OAAO,CAAC,IAAI,CAAC,CAkHf;AAYD;;;;;GAKG;AACH,wBAAsB,+BAA+B,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAsB3G;AAED,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,wBAAwB,EACjC,YAAY,EAAE,6BAA6B,GAC1C,MAAM,IAAI,CA2FZ"}
@@ -1,10 +1,14 @@
1
- import { peekSessionSandbox } from "../sandbox/session-sandbox.js";
1
+ import { peekSessionSandbox, sessionLayout } from "../sandbox/session-sandbox.js";
2
+ import { peekSessionEnvironment } from "./environment-state-processor.js";
2
3
  import { isMeaningfulToolName } from "./first-exec-capture.js";
3
4
  //#region src/session/filesystem-capture.ts
5
+ /** Exit code of `GIT_CHANGED_FILES_SCRIPT` when the checkout is not there: the repository is skipped, not failed. */
6
+ const CHECKOUT_MISSING_EXIT_CODE = 3;
4
7
  const GIT_CHANGED_FILES_SCRIPT = `
5
8
  set -e
6
9
  workdir=$1
7
10
  base_branch=$2
11
+ test -d "$workdir" || exit ${CHECKOUT_MISSING_EXIT_CODE}
8
12
  base=HEAD
9
13
  if merge_base=$(git -C "$workdir" merge-base HEAD "origin/$base_branch" 2>/dev/null); then
10
14
  base=$merge_base
@@ -13,6 +17,26 @@ git -C "$workdir" diff --name-only -z --find-renames --diff-filter=ACMRTUXB "$ba
13
17
  git -C "$workdir" ls-files --others --exclude-standard -z
14
18
  `;
15
19
  const ARTIFACTS_LIST_COMMAND = "cd \"$1\" && test -d .artifacts && find .artifacts -type f -print0 || true";
20
+ /**
21
+ * Regular files directly under the workspace root written since the session
22
+ * was created ($2 is a UTC `touch -t` stamp, `YYYYMMDDhhmm.SS`), such as the
23
+ * agent's notes. Files the template shipped with predate the session and stay
24
+ * out; the checkouts are covered by git. Dotfiles are never listed: a listed
25
+ * path becomes readable through the thread's file route, and the root is where
26
+ * setup leaves `.env`, `.pgpass`, `.netrc` and other credential files.
27
+ * `touch -t` plus `-newer` works on GNU and BSD find alike; `-newermt
28
+ * "@epoch"` is GNU only.
29
+ */
30
+ const ROOT_FILES_LIST_COMMAND = [
31
+ "cd \"$1\" && ref=$(mktemp) && TZ=UTC touch -t \"$2\" \"$ref\"",
32
+ "&& find . -mindepth 1 -maxdepth 1 -type f ! -name '.*' -newer \"$ref\" -print0",
33
+ "; status=$?; rm -f \"$ref\"; exit $status"
34
+ ].join(" ");
35
+ /** `touch -t` stamp of a moment, in UTC. */
36
+ function touchStamp(date) {
37
+ const two = (value) => String(value).padStart(2, "0");
38
+ return `${date.getUTCFullYear()}${two(date.getUTCMonth() + 1)}${two(date.getUTCDate())}${two(date.getUTCHours())}${two(date.getUTCMinutes())}.${two(date.getUTCSeconds())}`;
39
+ }
16
40
  function parseFilesystemCaptureFiles(output) {
17
41
  const files = /* @__PURE__ */ new Map();
18
42
  for (const record of output.split("\0")) {
@@ -32,34 +56,69 @@ async function captureSessionFilesystem(session, { filesystem, sourceControl },
32
56
  const entry = peekSessionSandbox(sourceSession.id);
33
57
  if (!entry) return;
34
58
  if (entry.sandbox.status !== "running") return;
35
- const workdir = entry.workdir;
36
- if (!workdir) return;
37
- const result = await sandbox.executeCommand("sh", [
59
+ const layout = sessionLayout(entry);
60
+ if (!layout) return;
61
+ const multiRepo = layout.repos.length > 1;
62
+ const environment = multiRepo ? peekSessionEnvironment(sourceSession.sessionId) : void 0;
63
+ const ownRepo = entry.ownRepo.toLowerCase();
64
+ const baseBranchFor = (repo) => {
65
+ if (repo.slug.toLowerCase() === ownRepo) return sourceSession.baseBranch;
66
+ return (environment?.repositories.find((candidate) => candidate.slug.toLowerCase() === repo.slug.toLowerCase()))?.defaultBranch ?? "HEAD";
67
+ };
68
+ const targets = layout.repos.map((repo) => ({
69
+ repo,
70
+ baseBranch: baseBranchFor(repo)
71
+ }));
72
+ const executeCommand = sandbox.executeCommand.bind(sandbox);
73
+ const results = await Promise.all(targets.map(({ repo, baseBranch }) => executeCommand("sh", [
38
74
  "-c",
39
75
  GIT_CHANGED_FILES_SCRIPT,
40
76
  "mastracode-changed-files",
41
- workdir,
42
- sourceSession.baseBranch
43
- ], { timeout: 3e4 });
44
- if (result.exitCode !== 0) {
45
- console.warn("[Factory filesystem capture] Unable to inspect Git changes.", result.stderr);
46
- return;
77
+ repo.dir,
78
+ baseBranch
79
+ ], { timeout: 3e4 })));
80
+ const files = /* @__PURE__ */ new Map();
81
+ let captured = 0;
82
+ for (const [index, result] of results.entries()) {
83
+ if (result.exitCode === CHECKOUT_MISSING_EXIT_CODE) continue;
84
+ if (result.exitCode !== 0) {
85
+ console.warn("[Factory filesystem capture] Unable to inspect Git changes.", result.stderr);
86
+ return;
87
+ }
88
+ captured += 1;
89
+ const prefix = targets[index].repo.prefix;
90
+ for (const file of parseFilesystemCaptureFiles(result.stdout)) files.set(`${prefix}${file.path}`, { path: `${prefix}${file.path}` });
47
91
  }
48
- const artifacts = await sandbox.executeCommand("sh", [
92
+ if (captured === 0) return;
93
+ const artifacts = await executeCommand("sh", [
49
94
  "-c",
50
95
  ARTIFACTS_LIST_COMMAND,
51
96
  "sh",
52
- workdir
97
+ layout.root
53
98
  ], { timeout: 3e4 });
54
99
  if (artifacts.exitCode !== 0) {
55
100
  console.warn("[Factory filesystem capture] Unable to list workspace artifacts.", artifacts.stderr);
56
101
  return;
57
102
  }
58
- const files = new Map(parseFilesystemCaptureFiles(result.stdout).map((file) => [file.path, file]));
59
103
  for (const path of artifacts.stdout.split("\0")) {
60
104
  const normalizedPath = path.replace(/^\.\//, "");
61
105
  if (normalizedPath) files.set(normalizedPath, { path: normalizedPath });
62
106
  }
107
+ if (multiRepo) {
108
+ const since = touchStamp(sourceSession.createdAt);
109
+ const rootFiles = await executeCommand("sh", [
110
+ "-c",
111
+ ROOT_FILES_LIST_COMMAND,
112
+ "sh",
113
+ layout.root,
114
+ since
115
+ ], { timeout: 3e4 });
116
+ if (rootFiles.exitCode !== 0) console.warn("[Factory filesystem capture] Unable to list workspace root files.", rootFiles.stderr);
117
+ else for (const path of rootFiles.stdout.split("\0")) {
118
+ const normalizedPath = path.replace(/^\.\//, "");
119
+ if (normalizedPath && !normalizedPath.startsWith(".")) files.set(normalizedPath, { path: normalizedPath });
120
+ }
121
+ }
63
122
  await filesystem.replaceFiles({
64
123
  resourceId,
65
124
  threadId,
@@ -167,6 +226,6 @@ function observeSessionFilesystem(session, dependencies) {
167
226
  };
168
227
  }
169
228
  //#endregion
170
- export { captureSessionFilesystem, observeSessionFilesystem, parseFilesystemCaptureFiles, waitForPendingFilesystemCapture };
229
+ export { ROOT_FILES_LIST_COMMAND, captureSessionFilesystem, observeSessionFilesystem, parseFilesystemCaptureFiles, touchStamp, waitForPendingFilesystemCapture };
171
230
 
172
231
  //# sourceMappingURL=filesystem-capture.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"filesystem-capture.js","names":[],"sources":["../../src/session/filesystem-capture.ts"],"sourcesContent":["import type { AgentControllerEvent, SessionBeforeAgentEndListener } from '@mastra/core/agent-controller';\nimport type { WorkspaceSandbox } from '@mastra/core/workspace';\nimport { peekSessionSandbox } from '../sandbox/session-sandbox.js';\n\nimport type { FilesystemFile, FilesystemStorage } from '../storage/domains/filesystem/base.js';\nimport type { SourceControlStorageHandle } from '../storage/domains/source-control/base.js';\nimport { isMeaningfulToolName } from './first-exec-capture.js';\n\nconst GIT_CHANGED_FILES_SCRIPT = `\nset -e\nworkdir=$1\nbase_branch=$2\nbase=HEAD\nif merge_base=$(git -C \"$workdir\" merge-base HEAD \"origin/$base_branch\" 2>/dev/null); then\n base=$merge_base\nfi\ngit -C \"$workdir\" diff --name-only -z --find-renames --diff-filter=ACMRTUXB \"$base\"\ngit -C \"$workdir\" ls-files --others --exclude-standard -z\n`;\nconst ARTIFACTS_LIST_COMMAND = 'cd \"$1\" && test -d .artifacts && find .artifacts -type f -print0 || true';\n\nexport interface FilesystemCaptureSession {\n readonly identity: { getResourceId(): string };\n readonly thread: { requireId(): string };\n getWorkspace(): { sandbox?: Pick<WorkspaceSandbox, 'executeCommand'> } | undefined;\n onBeforeAgentEnd(listener: SessionBeforeAgentEndListener): () => void;\n subscribe(listener: (event: AgentControllerEvent) => void): () => void;\n}\n\nexport interface FilesystemCaptureDependencies {\n filesystem: Pick<FilesystemStorage, 'replaceFiles'>;\n sourceControl: {\n sessions: Pick<SourceControlStorageHandle['sessions'], 'getBySessionId'>;\n };\n}\n\nexport function parseFilesystemCaptureFiles(output: string): FilesystemFile[] {\n const files = new Map<string, FilesystemFile>();\n for (const record of output.split('\\0')) {\n if (!record) continue;\n const path = record.replace(/^\\.\\//, '');\n if (path) files.set(path, { path });\n }\n\n return [...files.values()].toSorted((a, b) => a.path.localeCompare(b.path));\n}\n\nexport async function captureSessionFilesystem(\n session: FilesystemCaptureSession,\n { filesystem, sourceControl }: FilesystemCaptureDependencies,\n ids?: { resourceId: string; threadId: string },\n): Promise<void> {\n try {\n // Scheduled captures pass ids read at schedule time: a session deleted\n // before the queued capture runs has already cleared its thread.\n const resourceId = ids?.resourceId ?? session.identity.getResourceId();\n const threadId = ids?.threadId ?? session.thread.requireId();\n const sourceSession = await sourceControl.sessions.getBySessionId(resourceId);\n // Chat-only sessions run without a workspace; there is nothing to capture.\n const sandbox = session.getWorkspace()?.sandbox;\n if (!sourceSession || !sandbox?.executeCommand) return;\n // The live workdir comes from the per-process memo (the deterministic\n // truth) ONLY — never the persisted observability column, which a row\n // written under a previous provider can point at a workdir that no\n // longer exists (the stale-workdir incident class). No memo entry means\n // no live sandbox worth capturing in this replica; capture is\n // best-effort telemetry, so skip.\n const entry = peekSessionSandbox(sourceSession.id);\n if (!entry) return;\n // Telemetry must never provision a VM: executeCommand lazily starts the\n // sandbox via ensureRunning, so a chat turn that never touched the\n // workspace would otherwise boot (and clone into) a fresh VM just to\n // capture an empty git status. Only capture when the turn already has a\n // running sandbox.\n if (entry.sandbox.status !== 'running') return;\n // Running-but-unresolved should not happen (the start hook resolves the\n // workdir), but capture is best-effort — skip rather than guess.\n const workdir = entry.workdir;\n if (!workdir) return;\n\n const result = await sandbox.executeCommand(\n 'sh',\n ['-c', GIT_CHANGED_FILES_SCRIPT, 'mastracode-changed-files', workdir, sourceSession.baseBranch],\n { timeout: 30_000 },\n );\n if (result.exitCode !== 0) {\n console.warn('[Factory filesystem capture] Unable to inspect Git changes.', result.stderr);\n return;\n }\n\n const artifacts = await sandbox.executeCommand('sh', ['-c', ARTIFACTS_LIST_COMMAND, 'sh', workdir], {\n timeout: 30_000,\n });\n if (artifacts.exitCode !== 0) {\n console.warn('[Factory filesystem capture] Unable to list workspace artifacts.', artifacts.stderr);\n return;\n }\n\n const files = new Map(parseFilesystemCaptureFiles(result.stdout).map(file => [file.path, file]));\n for (const path of artifacts.stdout.split('\\0')) {\n const normalizedPath = path.replace(/^\\.\\//, '');\n if (normalizedPath) {\n files.set(normalizedPath, { path: normalizedPath });\n }\n }\n\n await filesystem.replaceFiles({\n resourceId,\n threadId,\n files: [...files.values()].toSorted((a, b) => a.path.localeCompare(b.path)),\n });\n } catch (error) {\n console.warn('[Factory filesystem capture] Unable to persist files.', error);\n }\n}\n\n/**\n * In-flight capture chains keyed by session resource id. Serves two purposes:\n * readers of the persisted file listing (the /web/workspace routes) await the\n * capture for the turn that just ended instead of racing it, and captures for\n * the same resource stay sequential (last write wins) even when multiple\n * scoped sessions observe the same resource id. In-process only: the factory\n * server that runs the session controller also serves those routes.\n */\nconst pendingCaptures = new Map<string, Promise<void>>();\n\n/**\n * Resolve once the session's in-flight filesystem capture (if any) has\n * persisted, or after `timeoutMs` so a reader that arrives mid-capture is\n * never blocked by the execs' worst-case 30s timeouts. Never rejects: the\n * capture body is fully try/catch contained.\n */\nexport async function waitForPendingFilesystemCapture(resourceId: string, timeoutMs = 10_000): Promise<void> {\n let pending = pendingCaptures.get(resourceId);\n if (!pending) return;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let timedOut = false;\n const deadline = new Promise<void>(resolve => {\n timer = setTimeout(() => {\n timedOut = true;\n resolve();\n }, timeoutMs);\n });\n try {\n // A turn that ends while we wait chains a successor capture; keep waiting\n // until the latest chain settles so the final snapshot is not lost.\n while (pending && !timedOut) {\n await Promise.race([pending, deadline]);\n const next = pendingCaptures.get(resourceId);\n pending = next === pending ? undefined : next;\n }\n } finally {\n clearTimeout(timer);\n }\n}\n\nexport function observeSessionFilesystem(\n session: FilesystemCaptureSession,\n dependencies: FilesystemCaptureDependencies,\n): () => void {\n let fallbackChain = Promise.resolve();\n // Capture only runs after a turn that actually touched the workspace: a\n // successful workspace tool call both proves the sandbox is awake and is\n // the only way the listing can have changed. Without this gate every chat\n // turn would run two sandbox execs — and since executeCommand resumes an\n // idle VM, a pure-chat turn would keep waking a sandbox it never used.\n // Same bookkeeping as first-exec capture: tool names resolve at tool_end\n // via the tool_start map, and suspended calls survive agent_end so a\n // resumed run's eventual completion still counts for that turn.\n let workspaceTouched = false;\n const toolNames = new Map<string, string>();\n const suspended = new Set<string>();\n const unsubscribeEvents = session.subscribe(event => {\n switch (event.type) {\n case 'tool_start': {\n toolNames.set(event.toolCallId, event.toolName);\n return;\n }\n case 'tool_suspended': {\n suspended.add(event.toolCallId);\n return;\n }\n case 'tool_suspension_cancelled': {\n toolNames.delete(event.toolCallId);\n suspended.delete(event.toolCallId);\n return;\n }\n case 'agent_end': {\n for (const id of toolNames.keys()) {\n if (!suspended.has(id)) toolNames.delete(id);\n }\n return;\n }\n case 'tool_end': {\n const toolName = toolNames.get(event.toolCallId);\n toolNames.delete(event.toolCallId);\n suspended.delete(event.toolCallId);\n if (event.isError || event.denied) return;\n if (isMeaningfulToolName(toolName)) workspaceTouched = true;\n return;\n }\n }\n });\n const unsubscribeEnd = session.onBeforeAgentEnd(() => {\n // onBeforeAgentEnd fires before agent_end is emitted, so every tool_end\n // of the finishing run has already been observed; read then reset.\n if (!workspaceTouched) return;\n workspaceTouched = false;\n // Chain so captures stay sequential (last write wins), but do NOT return\n // the chain: finishAgentRun awaits every listener before emitting\n // agent_end, and the capture's sandbox execs (git status + artifacts\n // find, 30s timeouts each) must not gate turn completion. Readers that\n // need the fresh listing await the pending capture through\n // waitForPendingFilesystemCapture instead. The un-returned chain cannot\n // leak an unhandled rejection: captureSessionFilesystem's entire body\n // runs inside its own try/catch, and disposal of this listener does not\n // cancel an in-flight chain, so a final turn's capture still persists.\n let resourceId: string | undefined;\n try {\n resourceId = session.identity.getResourceId();\n } catch {\n // No resource id means no route can address this session's listing;\n // keep a per-observer chain so captures still run sequentially.\n fallbackChain = fallbackChain.then(() => captureSessionFilesystem(session, dependencies));\n return;\n }\n // Extend the resource-level chain rather than a per-session one: sessions\n // are deduplicated by (resourceId, scope), so multiple scoped sessions can\n // observe the same resource, and their captures must not interleave.\n let threadId: string;\n try {\n threadId = session.thread.requireId();\n } catch {\n return;\n }\n const ids = { resourceId, threadId };\n const chain = (pendingCaptures.get(resourceId) ?? Promise.resolve()).then(() =>\n captureSessionFilesystem(session, dependencies, ids),\n );\n pendingCaptures.set(resourceId, chain);\n void chain.finally(() => {\n if (pendingCaptures.get(resourceId) === chain) {\n pendingCaptures.delete(resourceId);\n }\n });\n });\n return () => {\n unsubscribeEvents();\n unsubscribeEnd();\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"filesystem-capture.js","names":[],"sources":["../../src/session/filesystem-capture.ts"],"sourcesContent":["import type { AgentControllerEvent, SessionBeforeAgentEndListener } from '@mastra/core/agent-controller';\nimport type { WorkspaceSandbox } from '@mastra/core/workspace';\nimport { peekSessionSandbox, sessionLayout } from '../sandbox/session-sandbox.js';\nimport type { SessionLayoutRepository } from '../sandbox/session-sandbox.js';\n\nimport type { FilesystemFile, FilesystemStorage } from '../storage/domains/filesystem/base.js';\nimport type { SourceControlStorageHandle } from '../storage/domains/source-control/base.js';\nimport { peekSessionEnvironment } from './environment-state-processor.js';\nimport { isMeaningfulToolName } from './first-exec-capture.js';\n\n/** Exit code of `GIT_CHANGED_FILES_SCRIPT` when the checkout is not there: the repository is skipped, not failed. */\nconst CHECKOUT_MISSING_EXIT_CODE = 3;\nconst GIT_CHANGED_FILES_SCRIPT = `\nset -e\nworkdir=$1\nbase_branch=$2\ntest -d \"$workdir\" || exit ${CHECKOUT_MISSING_EXIT_CODE}\nbase=HEAD\nif merge_base=$(git -C \"$workdir\" merge-base HEAD \"origin/$base_branch\" 2>/dev/null); then\n base=$merge_base\nfi\ngit -C \"$workdir\" diff --name-only -z --find-renames --diff-filter=ACMRTUXB \"$base\"\ngit -C \"$workdir\" ls-files --others --exclude-standard -z\n`;\nconst ARTIFACTS_LIST_COMMAND = 'cd \"$1\" && test -d .artifacts && find .artifacts -type f -print0 || true';\n/**\n * Regular files directly under the workspace root written since the session\n * was created ($2 is a UTC `touch -t` stamp, `YYYYMMDDhhmm.SS`), such as the\n * agent's notes. Files the template shipped with predate the session and stay\n * out; the checkouts are covered by git. Dotfiles are never listed: a listed\n * path becomes readable through the thread's file route, and the root is where\n * setup leaves `.env`, `.pgpass`, `.netrc` and other credential files.\n * `touch -t` plus `-newer` works on GNU and BSD find alike; `-newermt\n * \"@epoch\"` is GNU only.\n */\nexport const ROOT_FILES_LIST_COMMAND = [\n 'cd \"$1\" && ref=$(mktemp) && TZ=UTC touch -t \"$2\" \"$ref\"',\n '&& find . -mindepth 1 -maxdepth 1 -type f ! -name \\'.*\\' -newer \"$ref\" -print0',\n '; status=$?; rm -f \"$ref\"; exit $status',\n].join(' ');\n\n/** `touch -t` stamp of a moment, in UTC. */\nexport function touchStamp(date: Date): string {\n const two = (value: number) => String(value).padStart(2, '0');\n return (\n `${date.getUTCFullYear()}${two(date.getUTCMonth() + 1)}${two(date.getUTCDate())}` +\n `${two(date.getUTCHours())}${two(date.getUTCMinutes())}.${two(date.getUTCSeconds())}`\n );\n}\n\nexport interface FilesystemCaptureSession {\n readonly identity: { getResourceId(): string };\n readonly thread: { requireId(): string };\n getWorkspace(): { sandbox?: Pick<WorkspaceSandbox, 'executeCommand'> } | undefined;\n onBeforeAgentEnd(listener: SessionBeforeAgentEndListener): () => void;\n subscribe(listener: (event: AgentControllerEvent) => void): () => void;\n}\n\nexport interface FilesystemCaptureDependencies {\n filesystem: Pick<FilesystemStorage, 'replaceFiles'>;\n sourceControl: {\n sessions: Pick<SourceControlStorageHandle['sessions'], 'getBySessionId'>;\n };\n}\n\nexport function parseFilesystemCaptureFiles(output: string): FilesystemFile[] {\n const files = new Map<string, FilesystemFile>();\n for (const record of output.split('\\0')) {\n if (!record) continue;\n const path = record.replace(/^\\.\\//, '');\n if (path) files.set(path, { path });\n }\n\n return [...files.values()].toSorted((a, b) => a.path.localeCompare(b.path));\n}\n\nexport async function captureSessionFilesystem(\n session: FilesystemCaptureSession,\n { filesystem, sourceControl }: FilesystemCaptureDependencies,\n ids?: { resourceId: string; threadId: string },\n): Promise<void> {\n try {\n // Scheduled captures pass ids read at schedule time: a session deleted\n // before the queued capture runs has already cleared its thread.\n const resourceId = ids?.resourceId ?? session.identity.getResourceId();\n const threadId = ids?.threadId ?? session.thread.requireId();\n const sourceSession = await sourceControl.sessions.getBySessionId(resourceId);\n // Chat-only sessions run without a workspace; there is nothing to capture.\n const sandbox = session.getWorkspace()?.sandbox;\n if (!sourceSession || !sandbox?.executeCommand) return;\n // The live workdir comes from the per-process memo (the deterministic\n // truth) ONLY — never the persisted observability column, which a row\n // written under a previous provider can point at a workdir that no\n // longer exists (the stale-workdir incident class). No memo entry means\n // no live sandbox worth capturing in this replica; capture is\n // best-effort telemetry, so skip.\n const entry = peekSessionSandbox(sourceSession.id);\n if (!entry) return;\n // Telemetry must never provision a VM: executeCommand lazily starts the\n // sandbox via ensureRunning, so a chat turn that never touched the\n // workspace would otherwise boot (and clone into) a fresh VM just to\n // capture an empty git status. Only capture when the turn already has a\n // running sandbox.\n if (entry.sandbox.status !== 'running') return;\n // Running-but-unresolved should not happen (the start hook resolves the\n // workdir), but capture is best-effort: skip rather than guess.\n const layout = sessionLayout(entry);\n if (!layout) return;\n\n // One git pass per checkout, in parallel so three repositories stay well\n // inside the 10 s readers wait for a pending capture. The session's own\n // repository compares against the session base branch; the others sit on\n // their default branch and compare against that. A repository whose\n // checkout is missing (exit 3) is skipped: it has nothing to list and\n // must not block the others. Any other failure is treated as transient\n // and leaves the previous listing in place.\n const multiRepo = layout.repos.length > 1;\n const environment = multiRepo ? peekSessionEnvironment(sourceSession.sessionId) : undefined;\n const ownRepo = entry.ownRepo.toLowerCase();\n // The boot records the state at its end, after every setup command; until\n // then a secondary checkout compares against its own `origin/HEAD`.\n const baseBranchFor = (repo: SessionLayoutRepository): string => {\n if (repo.slug.toLowerCase() === ownRepo) return sourceSession.baseBranch;\n const state = environment?.repositories.find(\n candidate => candidate.slug.toLowerCase() === repo.slug.toLowerCase(),\n );\n return state?.defaultBranch ?? 'HEAD';\n };\n const targets = layout.repos.map(repo => ({ repo, baseBranch: baseBranchFor(repo) }));\n const executeCommand = sandbox.executeCommand.bind(sandbox);\n const results = await Promise.all(\n targets.map(({ repo, baseBranch }) =>\n executeCommand('sh', ['-c', GIT_CHANGED_FILES_SCRIPT, 'mastracode-changed-files', repo.dir, baseBranch], {\n timeout: 30_000,\n }),\n ),\n );\n const files = new Map<string, FilesystemFile>();\n let captured = 0;\n for (const [index, result] of results.entries()) {\n if (result.exitCode === CHECKOUT_MISSING_EXIT_CODE) continue;\n if (result.exitCode !== 0) {\n console.warn('[Factory filesystem capture] Unable to inspect Git changes.', result.stderr);\n return;\n }\n captured += 1;\n const prefix = targets[index]!.repo.prefix;\n for (const file of parseFilesystemCaptureFiles(result.stdout)) {\n files.set(`${prefix}${file.path}`, { path: `${prefix}${file.path}` });\n }\n }\n if (captured === 0) return;\n\n const artifacts = await executeCommand('sh', ['-c', ARTIFACTS_LIST_COMMAND, 'sh', layout.root], {\n timeout: 30_000,\n });\n if (artifacts.exitCode !== 0) {\n console.warn('[Factory filesystem capture] Unable to list workspace artifacts.', artifacts.stderr);\n return;\n }\n for (const path of artifacts.stdout.split('\\0')) {\n const normalizedPath = path.replace(/^\\.\\//, '');\n if (normalizedPath) {\n files.set(normalizedPath, { path: normalizedPath });\n }\n }\n\n // With several checkouts the root is a plain directory nothing else lists.\n // A failed listing is logged and the git and artifact results still land.\n if (multiRepo) {\n const since = touchStamp(sourceSession.createdAt);\n const rootFiles = await executeCommand('sh', ['-c', ROOT_FILES_LIST_COMMAND, 'sh', layout.root, since], {\n timeout: 30_000,\n });\n if (rootFiles.exitCode !== 0) {\n console.warn('[Factory filesystem capture] Unable to list workspace root files.', rootFiles.stderr);\n } else {\n for (const path of rootFiles.stdout.split('\\0')) {\n const normalizedPath = path.replace(/^\\.\\//, '');\n if (normalizedPath && !normalizedPath.startsWith('.')) {\n files.set(normalizedPath, { path: normalizedPath });\n }\n }\n }\n }\n\n await filesystem.replaceFiles({\n resourceId,\n threadId,\n files: [...files.values()].toSorted((a, b) => a.path.localeCompare(b.path)),\n });\n } catch (error) {\n console.warn('[Factory filesystem capture] Unable to persist files.', error);\n }\n}\n\n/**\n * In-flight capture chains keyed by session resource id. Serves two purposes:\n * readers of the persisted file listing (the /web/workspace routes) await the\n * capture for the turn that just ended instead of racing it, and captures for\n * the same resource stay sequential (last write wins) even when multiple\n * scoped sessions observe the same resource id. In-process only: the factory\n * server that runs the session controller also serves those routes.\n */\nconst pendingCaptures = new Map<string, Promise<void>>();\n\n/**\n * Resolve once the session's in-flight filesystem capture (if any) has\n * persisted, or after `timeoutMs` so a reader that arrives mid-capture is\n * never blocked by the execs' worst-case 30s timeouts. Never rejects: the\n * capture body is fully try/catch contained.\n */\nexport async function waitForPendingFilesystemCapture(resourceId: string, timeoutMs = 10_000): Promise<void> {\n let pending = pendingCaptures.get(resourceId);\n if (!pending) return;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let timedOut = false;\n const deadline = new Promise<void>(resolve => {\n timer = setTimeout(() => {\n timedOut = true;\n resolve();\n }, timeoutMs);\n });\n try {\n // A turn that ends while we wait chains a successor capture; keep waiting\n // until the latest chain settles so the final snapshot is not lost.\n while (pending && !timedOut) {\n await Promise.race([pending, deadline]);\n const next = pendingCaptures.get(resourceId);\n pending = next === pending ? undefined : next;\n }\n } finally {\n clearTimeout(timer);\n }\n}\n\nexport function observeSessionFilesystem(\n session: FilesystemCaptureSession,\n dependencies: FilesystemCaptureDependencies,\n): () => void {\n let fallbackChain = Promise.resolve();\n // Capture only runs after a turn that actually touched the workspace: a\n // successful workspace tool call both proves the sandbox is awake and is\n // the only way the listing can have changed. Without this gate every chat\n // turn would run two sandbox execs — and since executeCommand resumes an\n // idle VM, a pure-chat turn would keep waking a sandbox it never used.\n // Same bookkeeping as first-exec capture: tool names resolve at tool_end\n // via the tool_start map, and suspended calls survive agent_end so a\n // resumed run's eventual completion still counts for that turn.\n let workspaceTouched = false;\n const toolNames = new Map<string, string>();\n const suspended = new Set<string>();\n const unsubscribeEvents = session.subscribe(event => {\n switch (event.type) {\n case 'tool_start': {\n toolNames.set(event.toolCallId, event.toolName);\n return;\n }\n case 'tool_suspended': {\n suspended.add(event.toolCallId);\n return;\n }\n case 'tool_suspension_cancelled': {\n toolNames.delete(event.toolCallId);\n suspended.delete(event.toolCallId);\n return;\n }\n case 'agent_end': {\n for (const id of toolNames.keys()) {\n if (!suspended.has(id)) toolNames.delete(id);\n }\n return;\n }\n case 'tool_end': {\n const toolName = toolNames.get(event.toolCallId);\n toolNames.delete(event.toolCallId);\n suspended.delete(event.toolCallId);\n if (event.isError || event.denied) return;\n if (isMeaningfulToolName(toolName)) workspaceTouched = true;\n return;\n }\n }\n });\n const unsubscribeEnd = session.onBeforeAgentEnd(() => {\n // onBeforeAgentEnd fires before agent_end is emitted, so every tool_end\n // of the finishing run has already been observed; read then reset.\n if (!workspaceTouched) return;\n workspaceTouched = false;\n // Chain so captures stay sequential (last write wins), but do NOT return\n // the chain: finishAgentRun awaits every listener before emitting\n // agent_end, and the capture's sandbox execs (git status + artifacts\n // find, 30s timeouts each) must not gate turn completion. Readers that\n // need the fresh listing await the pending capture through\n // waitForPendingFilesystemCapture instead. The un-returned chain cannot\n // leak an unhandled rejection: captureSessionFilesystem's entire body\n // runs inside its own try/catch, and disposal of this listener does not\n // cancel an in-flight chain, so a final turn's capture still persists.\n let resourceId: string | undefined;\n try {\n resourceId = session.identity.getResourceId();\n } catch {\n // No resource id means no route can address this session's listing;\n // keep a per-observer chain so captures still run sequentially.\n fallbackChain = fallbackChain.then(() => captureSessionFilesystem(session, dependencies));\n return;\n }\n // Extend the resource-level chain rather than a per-session one: sessions\n // are deduplicated by (resourceId, scope), so multiple scoped sessions can\n // observe the same resource, and their captures must not interleave.\n let threadId: string;\n try {\n threadId = session.thread.requireId();\n } catch {\n return;\n }\n const ids = { resourceId, threadId };\n const chain = (pendingCaptures.get(resourceId) ?? Promise.resolve()).then(() =>\n captureSessionFilesystem(session, dependencies, ids),\n );\n pendingCaptures.set(resourceId, chain);\n void chain.finally(() => {\n if (pendingCaptures.get(resourceId) === chain) {\n pendingCaptures.delete(resourceId);\n }\n });\n });\n return () => {\n unsubscribeEvents();\n unsubscribeEnd();\n };\n}\n"],"mappings":"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"}
@@ -20,6 +20,6 @@ export declare function createSourceControlTools({ requestContext, providers, au
20
20
  requestContext: RequestContext;
21
21
  providers: readonly SourceControlToolProvider[];
22
22
  audit: AuditAgentEmitter;
23
- }): IntegrationTools;
23
+ }): Promise<IntegrationTools>;
24
24
  export {};
25
25
  //# sourceMappingURL=source-control-tools.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"source-control-tools.d.ts","sourceRoot":"","sources":["../../src/session/source-control-tools.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAMnE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACzE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAKhE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AAC5E,OAAO,KAAK,EAIV,0BAA0B,EAC3B,MAAM,2CAA2C,CAAC;AAQnD,MAAM,WAAW,yBAAyB;IACxC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,0BAA0B,CAAC;IACpC,cAAc,EAAE,cAAc,CAAC;CAChC;AAYD,KAAK,WAAW,GAAG,SAAS,GAAG,iBAAiB,GAAG,SAAS,CAAC;AAE7D,kGAAkG;AAClG,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAUrG;AAKD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CASxF;AAuFD,wBAAgB,wBAAwB,CAAC,EACvC,cAAc,EACd,SAAS,EACT,KAAK,GACN,EAAE;IACD,cAAc,EAAE,cAAc,CAAC;IAC/B,SAAS,EAAE,SAAS,yBAAyB,EAAE,CAAC;IAChD,KAAK,EAAE,iBAAiB,CAAC;CAC1B,GAAG,gBAAgB,CAibnB"}
1
+ {"version":3,"file":"source-control-tools.d.ts","sourceRoot":"","sources":["../../src/session/source-control-tools.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAMnE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACzE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAUhE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AAC5E,OAAO,KAAK,EAIV,0BAA0B,EAC3B,MAAM,2CAA2C,CAAC;AAUnD,MAAM,WAAW,yBAAyB;IACxC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,0BAA0B,CAAC;IACpC,cAAc,EAAE,cAAc,CAAC;CAChC;AAcD,KAAK,WAAW,GAAG,SAAS,GAAG,iBAAiB,GAAG,SAAS,CAAC;AAE7D,kGAAkG;AAClG,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAUrG;AAKD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CASxF;AAuJD,wBAAsB,wBAAwB,CAAC,EAC7C,cAAc,EACd,SAAS,EACT,KAAK,GACN,EAAE;IACD,cAAc,EAAE,cAAc,CAAC;IAC/B,SAAS,EAAE,SAAS,yBAAyB,EAAE,CAAC;IAChD,KAAK,EAAE,iBAAiB,CAAC;CAC1B,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA8c5B"}