@mastra/factory 0.25.0-alpha.4 → 0.25.0-alpha.5

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 (301) hide show
  1. package/dist/auth.js +1 -2
  2. package/dist/boards/define-board.js +1 -2
  3. package/dist/boards/index.js +1 -2
  4. package/dist/boards/registry.js +1 -2
  5. package/dist/boards/relocate.js +1 -2
  6. package/dist/boards/review.js +1 -2
  7. package/dist/boards/semantics.js +1 -2
  8. package/dist/boards/transition-policy.js +1 -2
  9. package/dist/boards/work-tool-rules.js +1 -2
  10. package/dist/boards/work-transition-policy.js +1 -2
  11. package/dist/boards/work.js +1 -2
  12. package/dist/environment/build-runner.js +1 -2
  13. package/dist/environment/build-schedule.js +1 -2
  14. package/dist/environment/build-workflow.js +1 -2
  15. package/dist/environment/build.js +1 -2
  16. package/dist/environment/heads.js +1 -2
  17. package/dist/factory.d.ts +4 -2
  18. package/dist/factory.d.ts.map +1 -1
  19. package/dist/factory.js +43 -16
  20. package/dist/factory.js.map +1 -1
  21. package/dist/feed-events.js +1 -2
  22. package/dist/index.js +2 -2
  23. package/dist/integrations/channel-thread-announcements.d.ts +8 -0
  24. package/dist/integrations/channel-thread-announcements.d.ts.map +1 -0
  25. package/dist/integrations/channel-thread-announcements.js +16 -0
  26. package/dist/integrations/channel-thread-announcements.js.map +1 -0
  27. package/dist/integrations/github/acceptance-labels.js +1 -2
  28. package/dist/integrations/github/app-identity.js +1 -2
  29. package/dist/integrations/github/commits.js +1 -2
  30. package/dist/integrations/github/config.js +1 -2
  31. package/dist/integrations/github/default-rules.js +2 -3
  32. package/dist/integrations/github/integration.js +3 -4
  33. package/dist/integrations/github/issue-reconciler.js +1 -2
  34. package/dist/integrations/github/pat.js +1 -2
  35. package/dist/integrations/github/provenance.d.ts.map +1 -1
  36. package/dist/integrations/github/provenance.js +2 -5
  37. package/dist/integrations/github/provenance.js.map +1 -1
  38. package/dist/integrations/github/reconcile-worker.js +1 -2
  39. package/dist/integrations/github/reconciliation-config.js +1 -2
  40. package/dist/integrations/github/routes.js +4 -5
  41. package/dist/integrations/github/rules.d.ts.map +1 -1
  42. package/dist/integrations/github/rules.js +14 -3
  43. package/dist/integrations/github/rules.js.map +1 -1
  44. package/dist/integrations/github/sandbox-release.js +1 -2
  45. package/dist/integrations/github/sandbox.js +1 -2
  46. package/dist/integrations/github/session-subscriptions.js +1 -2
  47. package/dist/integrations/github/settle-or-abort.js +1 -2
  48. package/dist/integrations/github/stale-reviews.js +1 -2
  49. package/dist/integrations/github/subscriptions.js +1 -2
  50. package/dist/integrations/github/token-refresh.js +1 -2
  51. package/dist/integrations/github/webhook.d.ts.map +1 -1
  52. package/dist/integrations/github/webhook.js +17 -3
  53. package/dist/integrations/github/webhook.js.map +1 -1
  54. package/dist/integrations/gitlab/agent-tools.js +1 -2
  55. package/dist/integrations/gitlab/api.js +1 -2
  56. package/dist/integrations/gitlab/default-rules.js +1 -2
  57. package/dist/integrations/gitlab/integration.js +2 -3
  58. package/dist/integrations/gitlab/issue-reconciler.js +1 -2
  59. package/dist/integrations/gitlab/merge-request-reconciler.js +1 -2
  60. package/dist/integrations/gitlab/reconciler.js +1 -2
  61. package/dist/integrations/gitlab/reconciliation-config.js +1 -2
  62. package/dist/integrations/gitlab/routes.js +1 -2
  63. package/dist/integrations/gitlab/rules.js +1 -2
  64. package/dist/integrations/gitlab/session-subscriptions.js +1 -2
  65. package/dist/integrations/gitlab/subscriptions.js +1 -2
  66. package/dist/integrations/gitlab/version-control.js +1 -2
  67. package/dist/integrations/gitlab/webhook-dispatch.js +1 -2
  68. package/dist/integrations/gitlab/webhook.js +1 -2
  69. package/dist/integrations/incidentio/agent-tools.js +1 -2
  70. package/dist/integrations/incidentio/api.js +1 -2
  71. package/dist/integrations/incidentio/claim.js +1 -2
  72. package/dist/integrations/incidentio/default-rules.js +1 -2
  73. package/dist/integrations/incidentio/intake.js +1 -2
  74. package/dist/integrations/incidentio/integration.js +1 -2
  75. package/dist/integrations/incidentio/issue-reconciler.js +1 -2
  76. package/dist/integrations/incidentio/reconciliation-config.js +1 -2
  77. package/dist/integrations/incidentio/routes.js +1 -2
  78. package/dist/integrations/incidentio/rules.js +1 -2
  79. package/dist/integrations/issue-reconcile-worker.js +1 -2
  80. package/dist/integrations/issue-reconciler.js +1 -2
  81. package/dist/integrations/jira/adf.js +1 -2
  82. package/dist/integrations/jira/agent-tools.js +1 -2
  83. package/dist/integrations/jira/api.js +1 -2
  84. package/dist/integrations/jira/claim.js +1 -2
  85. package/dist/integrations/jira/default-rules.js +1 -2
  86. package/dist/integrations/jira/integration.js +1 -2
  87. package/dist/integrations/jira/issue-reconciler.js +1 -2
  88. package/dist/integrations/jira/reconciliation-config.js +1 -2
  89. package/dist/integrations/jira/routes.js +1 -2
  90. package/dist/integrations/jira/rules.js +1 -2
  91. package/dist/integrations/linear/agent-tools.d.ts +1 -1
  92. package/dist/integrations/linear/agent-tools.d.ts.map +1 -1
  93. package/dist/integrations/linear/agent-tools.js +5 -5
  94. package/dist/integrations/linear/agent-tools.js.map +1 -1
  95. package/dist/integrations/linear/claim.js +1 -2
  96. package/dist/integrations/linear/default-rules.js +1 -2
  97. package/dist/integrations/linear/integration.d.ts +2 -0
  98. package/dist/integrations/linear/integration.d.ts.map +1 -1
  99. package/dist/integrations/linear/integration.js +2 -2
  100. package/dist/integrations/linear/integration.js.map +1 -1
  101. package/dist/integrations/linear/issue-reconciler.js +1 -2
  102. package/dist/integrations/linear/reconciliation-config.js +1 -2
  103. package/dist/integrations/linear/routes.js +1 -2
  104. package/dist/integrations/linear/rules.js +1 -2
  105. package/dist/integrations/platform/api-client.js +1 -2
  106. package/dist/integrations/platform/connect/routes.js +1 -2
  107. package/dist/integrations/platform/github/event-worker.js +6 -2
  108. package/dist/integrations/platform/github/event-worker.js.map +1 -1
  109. package/dist/integrations/platform/github/integration.js +1 -2
  110. package/dist/integrations/platform/gitlab/event-worker.js +1 -2
  111. package/dist/integrations/platform/gitlab/integration.js +1 -2
  112. package/dist/integrations/platform/incidentio/integration.js +1 -2
  113. package/dist/integrations/platform/jira/agent-tools.js +1 -2
  114. package/dist/integrations/platform/jira/integration.js +1 -2
  115. package/dist/integrations/platform/jira/routes.js +1 -2
  116. package/dist/integrations/platform/linear/event-worker.js +1 -2
  117. package/dist/integrations/platform/linear/integration.d.ts.map +1 -1
  118. package/dist/integrations/platform/linear/integration.js +2 -2
  119. package/dist/integrations/platform/linear/integration.js.map +1 -1
  120. package/dist/integrations/pull-request-head.js +1 -2
  121. package/dist/integrations/reconciliation-config.js +1 -2
  122. package/dist/integrations/slack/channel-reply-processor.d.ts.map +1 -1
  123. package/dist/integrations/slack/channel-reply-processor.js +11 -5
  124. package/dist/integrations/slack/channel-reply-processor.js.map +1 -1
  125. package/dist/integrations/slack/channel-session-link-processor.js +1 -2
  126. package/dist/integrations/slack/connect-route.js +1 -2
  127. package/dist/integrations/slack/emoji-shortcodes.generated.js +1 -2
  128. package/dist/integrations/slack/emoji.js +1 -2
  129. package/dist/integrations/slack/feed-publisher.js +1 -2
  130. package/dist/integrations/slack/integration.d.ts +5 -0
  131. package/dist/integrations/slack/integration.d.ts.map +1 -1
  132. package/dist/integrations/slack/integration.js +13 -2
  133. package/dist/integrations/slack/integration.js.map +1 -1
  134. package/dist/integrations/slack/reply-reminder-processor.js +1 -2
  135. package/dist/integrations/slack/reply-tool.js +1 -2
  136. package/dist/integrations/slack/share-file-tool.js +1 -2
  137. package/dist/integrations/slack/slack.js +2 -3
  138. package/dist/integrations/subscription-session.js +1 -2
  139. package/dist/integrations/workos/integration.js +1 -2
  140. package/dist/mcp/disconnect.js +1 -2
  141. package/dist/mcp/events.js +2 -3
  142. package/dist/mcp/routes.js +1 -2
  143. package/dist/packages/_internals/workspace/dist/index.js +1 -2
  144. package/dist/packages/_internals/workspace/dist/index.js.map +1 -1
  145. package/dist/platform-auth-host.js +1 -2
  146. package/dist/provider-display-name.js +1 -2
  147. package/dist/review-verdict.d.ts +1 -0
  148. package/dist/review-verdict.d.ts.map +1 -1
  149. package/dist/review-verdict.js +9 -3
  150. package/dist/review-verdict.js.map +1 -1
  151. package/dist/routes/attention-activity.js +1 -2
  152. package/dist/routes/attention-parked.js +1 -2
  153. package/dist/routes/attention-proposed.js +1 -2
  154. package/dist/routes/attention-providers.js +1 -2
  155. package/dist/routes/attention.js +1 -2
  156. package/dist/routes/automation-runs.js +1 -2
  157. package/dist/routes/config.d.ts +4 -0
  158. package/dist/routes/config.d.ts.map +1 -1
  159. package/dist/routes/config.js +7 -2
  160. package/dist/routes/config.js.map +1 -1
  161. package/dist/routes/contracts.js +1 -2
  162. package/dist/routes/custom-provider-source.js +1 -2
  163. package/dist/routes/fs.js +1 -2
  164. package/dist/routes/intake.js +1 -2
  165. package/dist/routes/knowledge.js +1 -2
  166. package/dist/routes/oauth.js +1 -2
  167. package/dist/routes/projects.js +1 -2
  168. package/dist/routes/provider-credentials.js +1 -2
  169. package/dist/routes/route.js +1 -2
  170. package/dist/routes/session-access.js +1 -2
  171. package/dist/routes/skills.js +1 -2
  172. package/dist/routes/source-control-sessions.js +5 -6
  173. package/dist/routes/source-control-settings.js +1 -2
  174. package/dist/routes/surface.js +2 -3
  175. package/dist/routes/telemetry.js +1 -2
  176. package/dist/routes/tenant-credentials.js +1 -2
  177. package/dist/routes/work-items.js +2 -3
  178. package/dist/rules/binding-context.js +1 -2
  179. package/dist/rules/decision-applicability.js +1 -2
  180. package/dist/rules/dispatch-errors.js +1 -2
  181. package/dist/rules/dispatcher.js +1 -2
  182. package/dist/rules/event-rules.js +1 -2
  183. package/dist/rules/processor.js +1 -2
  184. package/dist/rules/resolve.js +1 -2
  185. package/dist/rules/review-source-tool.js +1 -2
  186. package/dist/rules/start-coordinator.js +1 -2
  187. package/dist/rules/terminal-cleanup.js +1 -2
  188. package/dist/rules/tools.js +1 -2
  189. package/dist/rules/transition-service.js +1 -2
  190. package/dist/rules/types.d.ts +4 -1
  191. package/dist/rules/types.d.ts.map +1 -1
  192. package/dist/rules/types.js +1 -2
  193. package/dist/rules/types.js.map +1 -1
  194. package/dist/rules/validation.js +1 -2
  195. package/dist/sandbox/callback-factory-sandbox.js +1 -2
  196. package/dist/sandbox/git-ref.js +1 -2
  197. package/dist/sandbox/local-factory-sandbox.d.ts +12 -4
  198. package/dist/sandbox/local-factory-sandbox.d.ts.map +1 -1
  199. package/dist/sandbox/local-factory-sandbox.js +32 -7
  200. package/dist/sandbox/local-factory-sandbox.js.map +1 -1
  201. package/dist/sandbox/materialization.js +1 -2
  202. package/dist/sandbox/session-retirement.js +1 -2
  203. package/dist/sandbox/session-sandbox.js +1 -2
  204. package/dist/sandbox/workdir.js +1 -2
  205. package/dist/secret-encryption.js +1 -2
  206. package/dist/server-error.js +1 -2
  207. package/dist/session/credential-policy.js +1 -2
  208. package/dist/session/default-model-hydration.js +1 -2
  209. package/dist/session/environment-repositories.js +1 -2
  210. package/dist/session/environment-state-processor.d.ts +0 -1
  211. package/dist/session/environment-state-processor.d.ts.map +1 -1
  212. package/dist/session/environment-state-processor.js +2 -3
  213. package/dist/session/environment-state-processor.js.map +1 -1
  214. package/dist/session/factory-session.js +1 -2
  215. package/dist/session/filesystem-capture.js +2 -3
  216. package/dist/session/filesystem-capture.js.map +1 -1
  217. package/dist/session/first-exec-capture.js +2 -3
  218. package/dist/session/first-exec-capture.js.map +1 -1
  219. package/dist/session/first-message-capture.js +1 -2
  220. package/dist/session/live-sessions.js +1 -2
  221. package/dist/session/memory-settings-hydration.js +1 -2
  222. package/dist/session/org-seed.js +1 -2
  223. package/dist/session/reply-credential-processor.js +1 -2
  224. package/dist/session/resource-migration.js +1 -2
  225. package/dist/session/run-audit.js +1 -2
  226. package/dist/session/session-threads.js +1 -2
  227. package/dist/session/session-title.js +1 -2
  228. package/dist/session/shell-commands.js +1 -2
  229. package/dist/session/source-control-tools.js +2 -3
  230. package/dist/session/thinking-default.js +1 -2
  231. package/dist/session/thread-session-stamp.js +1 -2
  232. package/dist/session/thread-title-mirror.js +1 -2
  233. package/dist/session/thread-title-mirror.js.map +1 -1
  234. package/dist/session/work-item-repository.js +1 -2
  235. package/dist/session-owner-signal.js +1 -2
  236. package/dist/skills/catalog.js +1 -2
  237. package/dist/skills/service.js +1 -2
  238. package/dist/spa-static.js +1 -2
  239. package/dist/state-signing.js +1 -2
  240. package/dist/storage/domains/audit/actions.js +1 -2
  241. package/dist/storage/domains/audit/actors.js +1 -2
  242. package/dist/storage/domains/audit/agent-audit.js +1 -2
  243. package/dist/storage/domains/audit/base.js +1 -2
  244. package/dist/storage/domains/audit/domain.js +1 -2
  245. package/dist/storage/domains/audit/wire.js +1 -2
  246. package/dist/storage/domains/channel-identity/base.js +1 -2
  247. package/dist/storage/domains/comments/activity.js +1 -2
  248. package/dist/storage/domains/comments/actor.js +1 -2
  249. package/dist/storage/domains/comments/base.js +1 -2
  250. package/dist/storage/domains/comments/domain.js +1 -2
  251. package/dist/storage/domains/comments/feed-context.js +1 -2
  252. package/dist/storage/domains/comments/parse.js +1 -2
  253. package/dist/storage/domains/comments/routes.js +1 -2
  254. package/dist/storage/domains/comments/schema.js +1 -2
  255. package/dist/storage/domains/comments/wire.js +1 -2
  256. package/dist/storage/domains/credentials/base.d.ts +4 -1
  257. package/dist/storage/domains/credentials/base.d.ts.map +1 -1
  258. package/dist/storage/domains/credentials/base.js +40 -44
  259. package/dist/storage/domains/credentials/base.js.map +1 -1
  260. package/dist/storage/domains/custom-providers/base.js +1 -2
  261. package/dist/storage/domains/filesystem/base.js +1 -2
  262. package/dist/storage/domains/intake/base.js +1 -2
  263. package/dist/storage/domains/integrations/base.js +1 -2
  264. package/dist/storage/domains/memory-settings/base.js +1 -2
  265. package/dist/storage/domains/model-defaults/base.js +1 -2
  266. package/dist/storage/domains/projects/base.js +1 -2
  267. package/dist/storage/domains/queue-health/base.js +1 -2
  268. package/dist/storage/domains/source-control/base.js +1 -2
  269. package/dist/storage/domains/source-control/inmemory.js +1 -2
  270. package/dist/storage/domains/work-items/base.js +1 -2
  271. package/dist/storage/domains/work-items/board-version.js +1 -2
  272. package/dist/storage/domains/work-items/metrics.js +1 -2
  273. package/dist/telemetry-types.js +1 -2
  274. package/dist/telemetry.js +1 -2
  275. package/dist/timing.js +1 -2
  276. package/dist/tools/annotations.js +1 -2
  277. package/dist/tools/approval-tools.js +1 -2
  278. package/dist/tools/deliver.js +2 -3
  279. package/dist/tools/index.js +1 -2
  280. package/dist/tools/messaging-tools.js +1 -2
  281. package/dist/tools/permissions.js +1 -2
  282. package/dist/tools/read-tools.js +1 -2
  283. package/dist/tools/reports.js +1 -2
  284. package/dist/tools/run-watch.js +1 -2
  285. package/dist/tools/sandbox-tools.js +1 -2
  286. package/dist/tools/scope.js +2 -3
  287. package/dist/tools/session-tools.js +1 -2
  288. package/dist/tools/sessions-state-processor.js +1 -2
  289. package/dist/tools/side-sessions.js +1 -2
  290. package/dist/tools/work-item-tools.js +1 -2
  291. package/dist/work-item-branch.js +1 -2
  292. package/dist/work-item-branch.js.map +1 -1
  293. package/dist/work-items/create-work-item.js +1 -2
  294. package/dist/work-items/decision-actions.js +1 -2
  295. package/dist/workspace.d.ts.map +1 -1
  296. package/dist/workspace.js +1 -3
  297. package/dist/workspace.js.map +1 -1
  298. package/factory-skills/factory-rereview/SKILL.md +12 -2
  299. package/factory-skills/factory-review/SKILL.md +12 -2
  300. package/factory-skills/factory-triage/SKILL.md +7 -7
  301. package/package.json +19 -17
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","names":[],"sources":["../../src/rules/types.ts"],"sourcesContent":["import type { ExternalWorkItemSource } from '../storage/domains/work-items/base.js';\n\nexport type WorkItemSource =\n | 'github-issue'\n | 'github-pr'\n | 'gitlab-issue'\n | 'gitlab-pr'\n | 'linear-issue'\n | 'jira-issue'\n | 'incidentio-follow-up'\n | 'manual';\n\nexport function externalSourceForWorkItem(\n source: WorkItemSource,\n sourceKey: string,\n url?: string,\n): ExternalWorkItemSource {\n const [integrationId, type] =\n source === 'github-pr'\n ? ['github', 'pull-request']\n : source === 'github-issue'\n ? ['github', 'issue']\n : source === 'gitlab-pr'\n ? ['gitlab', 'pull-request']\n : source === 'gitlab-issue'\n ? ['gitlab', 'issue']\n : source === 'linear-issue'\n ? ['linear', 'issue']\n : source === 'jira-issue'\n ? ['jira', 'issue']\n : source === 'incidentio-follow-up'\n ? ['incidentio', 'issue']\n : ['factory', 'manual'];\n return { integrationId, type, externalId: sourceKey, ...(url ? { url } : {}) };\n}\n\n/** The source label that holds an issue at rest until a maintainer decides; compared lowercased. */\nexport const NEEDS_APPROVAL_LABEL = 'status: needs approval';\nexport const AUTO_TRIAGED_LABEL = 'status: auto-triaged';\n\n// The label only holds a card at rest: once a person accepted it, or it sits in a working\n// lane only a person could have moved it into, the label is stale until the source catches up.\nexport function needsApproval(item: {\n metadata: Record<string, unknown> | null;\n stages?: readonly string[];\n acceptedAt?: Date | string | null;\n}): boolean {\n const labels = item.metadata?.labels;\n if (!Array.isArray(labels)) return false;\n return (\n labels.some(label => typeof label === 'string' && label.toLowerCase() === NEEDS_APPROVAL_LABEL) &&\n !item.acceptedAt &&\n (item.stages ?? ['intake']).every(stage => stage === 'intake' || stage === 'triage')\n );\n}\n\nexport function workItemSource(source: ExternalWorkItemSource | null): WorkItemSource {\n if (!source) return 'manual';\n if (source.integrationId === 'linear') return 'linear-issue';\n if (source.integrationId === 'gitlab') return source.type === 'pull-request' ? 'gitlab-pr' : 'gitlab-issue';\n if (source.integrationId === 'jira') return 'jira-issue';\n if (source.integrationId === 'incidentio') return 'incidentio-follow-up';\n // Only GitHub, GitLab, Linear, Jira, and incident.io have provider-specific\n // rules; anything else (a Slack thread, say) is a plain work item, not a\n // mislabeled GitHub issue.\n if (source.integrationId !== 'github') return 'manual';\n return source.type === 'pull-request' ? 'github-pr' : 'github-issue';\n}\n\n// Authored outside the write-access circle: a missing trust stamp fails closed until\n// the reconcile sweep backfills it, and Factory's own PRs pass through `factoryAuthored`.\nexport function externallyAuthored(item: { source: string; metadata: Record<string, unknown> | null }): boolean {\n if (!['github-pr', 'github-issue', 'gitlab-pr', 'gitlab-issue'].includes(item.source)) return false;\n if (item.metadata?.factoryAuthored === true) return false;\n return item.metadata?.authorTrusted !== true;\n}\n\nexport function externallyAuthoredWorkItem(item: {\n externalSource: ExternalWorkItemSource | null;\n metadata: Record<string, unknown> | null;\n}): boolean {\n return externallyAuthored({ source: workItemSource(item.externalSource), metadata: item.metadata });\n}\n\nexport const FACTORY_RULE_STAGES = ['intake', 'triage', 'planning', 'execute', 'review', 'done', 'canceled'] as const;\nexport type FactoryRuleStage = (typeof FACTORY_RULE_STAGES)[number] | (string & {});\n\n// Each role and the working stage its run holds the card in. Key order is the\n// seat pipeline order — Resume depth derives from it.\nexport const FACTORY_ROLE_STAGES = {\n triage: 'triage',\n plan: 'planning',\n work: 'execute',\n review: 'review',\n} as const satisfies Record<string, FactoryRuleStage>;\nexport type FactoryRole = keyof typeof FACTORY_ROLE_STAGES;\n\nexport function isFactoryRole(value: string): value is FactoryRole {\n return value in FACTORY_ROLE_STAGES;\n}\n\nexport const FACTORY_TRIAGE_TYPES = [\n 'bug',\n 'feature request',\n 'docs',\n 'question/support',\n 'maintenance',\n 'duplicate',\n 'resolved',\n 'invalid',\n 'spam',\n 'out-of-scope',\n 'other',\n] as const;\nexport type FactoryTriageType = (typeof FACTORY_TRIAGE_TYPES)[number];\n\nexport function isFactoryTriageType(value: unknown): value is FactoryTriageType {\n return typeof value === 'string' && FACTORY_TRIAGE_TYPES.some(type => type === value);\n}\n\nexport function isFactoryRuleStage(value: unknown): value is FactoryRuleStage {\n return typeof value === 'string' && FACTORY_RULE_STAGES.some(stage => stage === value);\n}\n\nexport function factoryRuleStage(stages: readonly string[]): FactoryRuleStage | undefined {\n const stage = stages.length === 1 ? stages[0] : undefined;\n return typeof stage === 'string' && stage.length > 0 ? stage : undefined;\n}\n\nexport function isTerminalFactoryRuleStage(stages: readonly string[]): boolean {\n const stage = factoryRuleStage(stages);\n return stage === 'done' || stage === 'canceled';\n}\n\n/** Working lanes hold cards with a seat engaged; Intake, Done and Canceled rest them. */\nexport function isWorkingFactoryRuleStage(stage: FactoryRuleStage): boolean {\n return stage !== 'intake' && !isTerminalFactoryRuleStage([stage]);\n}\n\n// Consulted only for the Intake exit: roles don't own lanes, so a card already\n// in a working or terminal lane stays put when a run starts.\nexport function factoryLaneForRole(role: string): FactoryRuleStage | undefined {\n return isFactoryRole(role) ? FACTORY_ROLE_STAGES[role] : undefined;\n}\n\nexport const FACTORY_RULE_BOARDS = ['work', 'review'] as const;\nexport type FactoryRuleBoard = (typeof FACTORY_RULE_BOARDS)[number] | (string & {});\n\nexport const FACTORY_RULE_SOURCES = [\n 'issue',\n 'pullRequest',\n 'gitlabIssue',\n 'gitlabPullRequest',\n 'linearIssue',\n 'jiraIssue',\n 'incidentioFollowUp',\n 'manual',\n] as const;\nexport type FactoryRuleSource = (typeof FACTORY_RULE_SOURCES)[number];\n\nexport const FACTORY_GITHUB_EVENTS = [\n 'issueOpened',\n 'issueEdited',\n 'issueClosed',\n 'issueCommentCreated',\n 'issueCommentEdited',\n 'issueCommentDeleted',\n 'pullRequestOpened',\n 'pullRequestUpdated',\n 'pullRequestCommentCreated',\n 'pullRequestReviewRequested',\n 'pullRequestReviewSubmitted',\n 'pullRequestMerged',\n 'pullRequestClosed',\n] as const;\nexport type FactoryGithubEventName = (typeof FACTORY_GITHUB_EVENTS)[number];\n\nexport const FACTORY_GITLAB_EVENTS = [\n 'issueOpened',\n 'issueUpdated',\n 'issueClosed',\n 'issueCommented',\n 'mergeRequestOpened',\n 'mergeRequestUpdated',\n 'mergeRequestCommented',\n 'mergeRequestMerged',\n 'mergeRequestClosed',\n] as const;\nexport type FactoryGitLabEventName = (typeof FACTORY_GITLAB_EVENTS)[number];\n\nexport const FACTORY_LINEAR_EVENTS = ['issueObserved', 'issueClosed'] as const;\nexport type FactoryLinearEventName = (typeof FACTORY_LINEAR_EVENTS)[number];\n\nexport const FACTORY_JIRA_EVENTS = ['issueObserved', 'issueClosed'] as const;\nexport type FactoryJiraEventName = (typeof FACTORY_JIRA_EVENTS)[number];\n\nexport const FACTORY_INCIDENTIO_EVENTS = ['followUpObserved', 'followUpClosed'] as const;\nexport type FactoryIncidentioEventName = (typeof FACTORY_INCIDENTIO_EVENTS)[number];\n\nexport type FactoryRuleJsonValue =\n | null\n | boolean\n | number\n | string\n | FactoryRuleJsonValue[]\n | { [key: string]: FactoryRuleJsonValue };\n\nexport interface FactoryRuleItemContext {\n id: string;\n source: WorkItemSource;\n sourceKey: string | null;\n parentWorkItemId: string | null;\n title: string;\n url: string | null;\n stages: readonly string[];\n acceptedAt: Date | null;\n /** Intake-stamped facts about the source — repository id, reporter login, labels. */\n metadata: Record<string, unknown> | null;\n}\n\nexport type FactoryRuleActor =\n | { type: 'human'; id: string }\n | { type: 'agent'; bindingId: string; role: string }\n | { type: 'github'; login: string; trusted: boolean; factoryAuthored: boolean }\n | { type: 'gitlab'; username: string; trusted: boolean; factoryAuthored: boolean }\n | { type: 'system'; id: string };\n\nexport interface FactoryRuleIngressIdentity {\n type: 'human' | 'agent' | 'toolResult' | 'github' | 'gitlab' | 'linear' | 'jira' | 'incidentio' | 'rule';\n id: string;\n}\n\nexport interface FactoryRuleCausalEntry {\n ingressId: string;\n decisionType: FactoryCommitDecision['type'];\n}\n\n/** The phase a card sits in, as its board declares it. */\nexport interface FactoryRulePhaseContext {\n id: string;\n kind: 'resting' | 'working' | 'terminal';\n /** Seat carrying a working phase; absent for resting and terminal phases. */\n role?: string;\n /** Outcome names the phase declares, e.g. `approved` or `canceled`; empty when it declares none. */\n outcomes: readonly string[];\n}\n\nexport interface FactoryRuleContextBase {\n tenant: { orgId: string; projectId: string };\n actor: FactoryRuleActor;\n ingress: FactoryRuleIngressIdentity;\n cause: string;\n causalChain: readonly FactoryRuleCausalEntry[];\n configVersion: string;\n}\n\nexport interface FactoryBoundRuleContext extends FactoryRuleContextBase {\n item: FactoryRuleItemContext;\n board: FactoryRuleBoard;\n itemRevision: number;\n}\n\nexport interface FactoryStageRuleContext extends FactoryBoundRuleContext {\n source: FactoryRuleSource;\n stage: FactoryRuleStage;\n fromStage: FactoryRuleStage;\n toStage: FactoryRuleStage;\n /** Project switch: Work items get an independent self-review before the change request opens. Unset means off. */\n selfReviewEnabled?: boolean;\n}\n\nexport interface FactoryToolResultRuleContext extends FactoryBoundRuleContext {\n toolName: string;\n threadId: string;\n assistantMessageId: string;\n toolCallId: string;\n result: {\n status: 'success' | 'error';\n value: FactoryRuleJsonValue;\n };\n}\n\nexport interface FactoryGithubRuleContext extends FactoryRuleContextBase {\n item?: FactoryRuleItemContext;\n board?: FactoryRuleBoard;\n itemRevision?: number;\n /** Where the card's board sends it when its tracked source closes. */\n boardClosure?: FactoryRuleBoardClosure;\n /** What the card's current phase means on its board; absent without a card or when the board does not declare the phase. */\n phase?: FactoryRulePhaseContext;\n /**\n * Board an issue's labels route it to, when the project has a matching label route and that board\n * is installed. Absent for pull requests and for issues whose labels select nothing.\n */\n intake?: FactoryRuleIntakeTarget;\n /**\n * Set on the one evaluation per pull request delivery that files the pull\n * request's own Review card. Opening a pull request concerns two cards — that\n * Review card and the Work item that authored the pull request — so the rule\n * answers for the two separately: the arrival carries this flag and is\n * committed against the authoring item when resolution found one, which is\n * what links the new card to it, while the authoring item's own evaluation\n * leaves the flag unset.\n */\n pullRequestIntake?: boolean;\n event: FactoryGithubEventName;\n deliveryId: string;\n factory: { createdAt: string };\n /** `installationId` is the GitHub App installation the delivery arrived through. */\n repository: { id: number; fullName: string; installationId?: number };\n issue?: {\n number: number;\n title: string;\n url: string;\n createdAt?: string;\n updatedAt?: string;\n assignees?: string[];\n labels?: string[];\n state?: 'open' | 'closed';\n /** GitHub close reason: `completed`, `not_planned`, or `duplicate`. */\n stateReason?: string;\n };\n issueChange?: { title: boolean; body: boolean };\n issueComment?: {\n id: number;\n body?: string;\n url?: string;\n author?: string;\n authorType?: string;\n createdAt?: string;\n updatedAt?: string;\n };\n pullRequest?: {\n number: number;\n title: string;\n url: string;\n createdAt?: string;\n state: 'open' | 'closed';\n draft: boolean;\n merged: boolean;\n assignees?: string[];\n requestedReviewers?: string[];\n labels?: string[];\n author?: string;\n factoryAuthored: boolean;\n headBranch: string;\n baseBranch: string;\n };\n /** Present on `pullRequestReviewRequested`: who review was (re-)requested from. */\n reviewRequest?: { reviewer: string; factoryReviewer: boolean };\n /** Present when a PR comment uses Factory's exact review command. */\n reviewCommand?: { command: 'review' | 're-review'; target: string };\n /** Present on `pullRequestReviewSubmitted`: the review that was just posted. */\n review?: { id: number; state: string; url: string; author?: string; body?: string };\n}\n\n/**\n * Where an intake source binding says new items from this source should land.\n * Absent when the source is unbound or bound without a board (built-in routing).\n */\nexport interface FactoryRuleIntakeTarget {\n board: string;\n initialPhase: string;\n}\n\n/**\n * Where a source-closed rule moves the card, from the `sourceClosed` mapping its installed board\n * declares. Absent when the card's board is not installed or declares no mapping.\n */\nexport interface FactoryRuleBoardClosure {\n /** Title of the card's board, for messages. */\n boardTitle: string;\n /** Terminal phase for a source that was completed. */\n completed: FactoryRuleBoardClosurePhase;\n /** Terminal phase for a source that was canceled or not planned. */\n canceled: FactoryRuleBoardClosurePhase;\n /** Whether the card already sits in a terminal phase of its board. */\n itemTerminal: boolean;\n}\n\nexport interface FactoryRuleBoardClosurePhase {\n phase: string;\n title: string;\n}\n\nexport interface FactoryGitLabRuleContext extends FactoryRuleContextBase {\n item?: FactoryRuleItemContext;\n board?: FactoryRuleBoard;\n itemRevision?: number;\n /** Where the card's board sends it when its tracked source closes. */\n boardClosure?: FactoryRuleBoardClosure;\n /** What the card's current phase means on its board; absent without a card or when the board does not declare the phase. */\n phase?: FactoryRulePhaseContext;\n intake?: FactoryRuleIntakeTarget;\n event: FactoryGitLabEventName;\n deliveryId: string;\n factory: { createdAt: string };\n repository: { id: number; fullName: string; host: string };\n issue?: {\n number: number;\n sourceKey: string;\n title: string;\n url: string;\n createdAt?: string;\n updatedAt?: string;\n assignees?: string[];\n labels?: string[];\n labelColors?: Record<string, string>;\n state?: 'open' | 'closed';\n author?: string;\n authorTrusted: boolean;\n };\n note?: {\n id: number;\n body?: string;\n url?: string;\n author?: string;\n createdAt?: string;\n updatedAt?: string;\n };\n mergeRequest?: {\n number: number;\n sourceKey: string;\n title: string;\n url: string;\n createdAt?: string;\n state: 'open' | 'closed';\n draft: boolean;\n merged: boolean;\n assignees?: string[];\n reviewers?: string[];\n labels?: string[];\n labelColors?: Record<string, string>;\n author?: string;\n authorTrusted: boolean;\n factoryAuthored: boolean;\n headBranch: string;\n baseBranch: string;\n };\n}\n\nexport interface FactoryLinearRuleContext extends FactoryRuleContextBase {\n item?: FactoryRuleItemContext;\n board?: FactoryRuleBoard;\n itemRevision?: number;\n /** Where the card's board sends it when its tracked source closes. */\n boardClosure?: FactoryRuleBoardClosure;\n /** What the card's current phase means on its board; absent without a card or when the board does not declare the phase. */\n phase?: FactoryRulePhaseContext;\n /** Bound board for the source this issue came from, when one is configured and installed. */\n intake?: FactoryRuleIntakeTarget;\n event: FactoryLinearEventName;\n issue: {\n id: string;\n identifier: string;\n title: string;\n url: string;\n state: string;\n stateType: string;\n priorityLabel: string;\n assignee: string | null;\n creator: string | null;\n team: string | null;\n labels: readonly string[];\n createdAt: string;\n updatedAt: string;\n sourceId?: string | null;\n projectId?: string | null;\n };\n}\n\nexport interface FactoryJiraRuleContext extends FactoryRuleContextBase {\n item?: FactoryRuleItemContext;\n board?: FactoryRuleBoard;\n itemRevision?: number;\n /** Where the card's board sends it when its tracked source closes. */\n boardClosure?: FactoryRuleBoardClosure;\n /** What the card's current phase means on its board; absent without a card or when the board does not declare the phase. */\n phase?: FactoryRulePhaseContext;\n /** Bound board for the source this issue came from, when one is configured and installed. */\n intake?: FactoryRuleIntakeTarget;\n event: FactoryJiraEventName;\n issue: {\n /** Stable issue reference — the direct integration's Jira id or the Platform-encoded issue reference. */\n id: string;\n identifier: string;\n title: string;\n url: string;\n state: string;\n stateType: string;\n priorityLabel: string;\n assignee: string | null;\n author: string | null;\n project: string | null;\n site: string | null;\n labels: readonly string[];\n createdAt: string;\n updatedAt: string;\n };\n}\n\nexport interface FactoryIncidentioRuleContext extends FactoryRuleContextBase {\n item?: FactoryRuleItemContext;\n board?: FactoryRuleBoard;\n itemRevision?: number;\n /** Where the card's board sends it when its tracked source closes. */\n boardClosure?: FactoryRuleBoardClosure;\n /** What the card's current phase means on its board; absent without a card or when the board does not declare the phase. */\n phase?: FactoryRulePhaseContext;\n /** Bound board for the source this follow-up came from, when one is configured and installed. */\n intake?: FactoryRuleIntakeTarget;\n event: FactoryIncidentioEventName;\n issue: {\n /** Stable item reference — the prefixed incident.io follow-up id the intake feed serves as `id`. */\n id: string;\n identifier: string;\n title: string;\n url: string;\n state: string;\n stateType: string;\n priorityLabel: string;\n assignee: string | null;\n author: string | null;\n /** Reference of the incident this follow-up belongs to, when available. */\n incident: string | null;\n labels: readonly string[];\n createdAt: string;\n updatedAt: string;\n };\n}\n\nexport type FactoryRuleHandler<TContext> = (\n context: Readonly<TContext>,\n) => FactoryRuleDecision | void | Promise<FactoryRuleDecision | void>;\n\nexport interface FactoryBoardRuleLeaf {\n onEnter?: FactoryRuleHandler<FactoryStageRuleContext>;\n onExit?: FactoryRuleHandler<FactoryStageRuleContext>;\n}\n\nexport type FactoryRuleRejectionCode =\n | 'forbidden'\n | 'invalid_transition'\n | 'missing_binding'\n | 'stale'\n | 'timeout'\n | 'rule_error'\n | 'causal_depth_exceeded'\n | 'repeated_transition'\n | 'approval_required';\n\nexport interface FactoryRuleRejectDecision {\n type: 'reject';\n code: FactoryRuleRejectionCode;\n reason: string;\n}\n\ninterface FactoryCommitDecisionBase {\n idempotencyKey: string;\n}\n\nexport interface FactoryTransitionDecision extends FactoryCommitDecisionBase {\n type: 'transition';\n board: FactoryRuleBoard;\n stage: FactoryRuleStage;\n /**\n * Delivered to the item's active session (waking it if idle) after the\n * transition commits. Skipped when the item has no active run binding, so\n * informational messages never fail the transition.\n */\n message?: { text: string; role?: string };\n /**\n * Runs the stage's entry rules even when the item is already in that stage.\n * A transition to the current stage is normally inert, because most callers\n * are correcting a board into a state it already holds. Re-entry is for the\n * opposite case: the stage's work is in flight and has been invalidated, so\n * it has to start over.\n */\n reenter?: boolean;\n}\n\nexport interface FactoryUpsertLinkedWorkItemDecision extends FactoryCommitDecisionBase {\n type: 'upsertLinkedWorkItem';\n board: FactoryRuleBoard;\n source: WorkItemSource;\n sourceKey: string;\n /**\n * Org-wide ownership key for the external record, when one Factory at a time\n * may hold a live card for it (a stable Linear issue id, for instance). The\n * store enforces it with a unique index, so a second project's materialization\n * is refused rather than duplicated.\n */\n claimKey?: string;\n title: string;\n url: string | null;\n stage: FactoryRuleStage;\n /**\n * File the card at `stage` as its first entry and run none of the board's\n * phase rules for it — no arrival, no destination entry. The card is filed\n * (or, if it already exists, moved) and left parked for a person; nothing is\n * started for it. For external records that arrive already past the board's\n * first step: a GitHub issue whose triage is already recorded, for instance.\n *\n * Placement is the whole decision. An existing card it reaches is moved to\n * that stage through the same relocation the label routes use, and its\n * metadata is left alone.\n */\n skipRules?: boolean;\n metadata?: Record<string, FactoryRuleJsonValue>;\n}\n\ninterface FactoryInvokeSkillDecisionBase extends FactoryCommitDecisionBase {\n type: 'invokeSkill';\n role: string;\n arguments?: string;\n precedingMessage?: string;\n cancelInFlight?: boolean;\n}\n\n/**\n * Starting an agent run. Most runs activate a skill, because the skill carries\n * the handoff contract later rules match on. A run whose completion is already\n * signalled some other way — Building finishes by opening a pull request, which\n * arrives as its own event — needs no contract, so it can carry a plain prompt\n * instead of an otherwise empty skill.\n */\nexport type FactoryInvokeSkillDecision = FactoryInvokeSkillDecisionBase &\n (\n | {\n skillName: string;\n prompt?: never;\n /**\n * Same-stage re-entry: the skill is already active in the card's live session,\n * so deliver a compact continuation that references it by name and carries only\n * the fresh arguments, instead of re-pasting the whole skill document. Only valid\n * for named-skill decisions — a plain prompt run has no active skill to resume.\n */\n resume?: boolean;\n }\n | { prompt: string; skillName?: never; resume?: never }\n );\n\nexport interface FactorySendMessageDecision extends FactoryCommitDecisionBase {\n type: 'sendMessage';\n /** Omitted: the card's live session, whichever seat holds it. Required with `prepareBinding`. */\n role?: string;\n message: string;\n priority?: 'medium' | 'high' | 'urgent';\n idleBehavior?: 'persist' | 'wake';\n prepareBinding?: boolean;\n}\n\nexport interface FactoryNotifyDecision extends FactoryCommitDecisionBase {\n type: 'notify';\n title: string;\n body?: string;\n level?: 'info' | 'warning' | 'error';\n}\n\n/**\n * Dismisses Factory change requests an approval has superseded on a GitHub\n * pull request. Only reviews carrying Factory's `Verdict: request changes`\n * marker from an identity other than the approving one are dismissed.\n */\nexport interface FactoryDismissStaleReviewsDecision extends FactoryCommitDecisionBase {\n type: 'dismissStaleReviews';\n installationId: number;\n repository: string;\n pullRequestNumber: number;\n approvingReviewId: string;\n approvingAuthor: string;\n}\n\nexport type FactoryCommitDecision =\n | FactoryTransitionDecision\n | FactoryDismissStaleReviewsDecision\n | FactoryUpsertLinkedWorkItemDecision\n | FactoryInvokeSkillDecision\n | FactorySendMessageDecision\n | FactoryNotifyDecision;\n\nexport type FactoryRuleDecision = FactoryRuleRejectDecision | FactoryCommitDecision;\n\nexport interface FactoryTransitionResultAccepted {\n status: 'accepted';\n transitionId: string;\n itemId: string;\n revision: number;\n stage: FactoryRuleStage;\n decisions: FactoryCommitDecision[];\n}\n\nexport interface FactoryTransitionResultRejected {\n status: 'rejected';\n transitionId: string;\n itemId: string;\n code: FactoryRuleRejectionCode;\n reason: string;\n}\n\nexport type FactoryTransitionResult = FactoryTransitionResultAccepted | FactoryTransitionResultRejected;\n\nexport function factoryRuleSourceForWorkItem(source: WorkItemSource): FactoryRuleSource {\n switch (source) {\n case 'github-issue':\n return 'issue';\n case 'github-pr':\n return 'pullRequest';\n case 'gitlab-issue':\n return 'gitlabIssue';\n case 'gitlab-pr':\n return 'gitlabPullRequest';\n case 'linear-issue':\n return 'linearIssue';\n case 'jira-issue':\n return 'jiraIssue';\n case 'incidentio-follow-up':\n return 'incidentioFollowUp';\n case 'manual':\n return 'manual';\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"types.js","names":[],"sources":["../../src/rules/types.ts"],"sourcesContent":["import type { ExternalWorkItemSource } from '../storage/domains/work-items/base.js';\n\nexport type WorkItemSource =\n | 'github-issue'\n | 'github-pr'\n | 'gitlab-issue'\n | 'gitlab-pr'\n | 'linear-issue'\n | 'jira-issue'\n | 'incidentio-follow-up'\n | 'manual';\n\nexport function externalSourceForWorkItem(\n source: WorkItemSource,\n sourceKey: string,\n url?: string,\n): ExternalWorkItemSource {\n const [integrationId, type] =\n source === 'github-pr'\n ? ['github', 'pull-request']\n : source === 'github-issue'\n ? ['github', 'issue']\n : source === 'gitlab-pr'\n ? ['gitlab', 'pull-request']\n : source === 'gitlab-issue'\n ? ['gitlab', 'issue']\n : source === 'linear-issue'\n ? ['linear', 'issue']\n : source === 'jira-issue'\n ? ['jira', 'issue']\n : source === 'incidentio-follow-up'\n ? ['incidentio', 'issue']\n : ['factory', 'manual'];\n return { integrationId, type, externalId: sourceKey, ...(url ? { url } : {}) };\n}\n\n/** The source label that holds an issue at rest until a maintainer decides; compared lowercased. */\nexport const NEEDS_APPROVAL_LABEL = 'status: needs approval';\nexport const AUTO_TRIAGED_LABEL = 'status: auto-triaged';\n\n// The label only holds a card at rest: once a person accepted it, or it sits in a working\n// lane only a person could have moved it into, the label is stale until the source catches up.\nexport function needsApproval(item: {\n metadata: Record<string, unknown> | null;\n stages?: readonly string[];\n acceptedAt?: Date | string | null;\n}): boolean {\n const labels = item.metadata?.labels;\n if (!Array.isArray(labels)) return false;\n return (\n labels.some(label => typeof label === 'string' && label.toLowerCase() === NEEDS_APPROVAL_LABEL) &&\n !item.acceptedAt &&\n (item.stages ?? ['intake']).every(stage => stage === 'intake' || stage === 'triage')\n );\n}\n\nexport function workItemSource(source: ExternalWorkItemSource | null): WorkItemSource {\n if (!source) return 'manual';\n if (source.integrationId === 'linear') return 'linear-issue';\n if (source.integrationId === 'gitlab') return source.type === 'pull-request' ? 'gitlab-pr' : 'gitlab-issue';\n if (source.integrationId === 'jira') return 'jira-issue';\n if (source.integrationId === 'incidentio') return 'incidentio-follow-up';\n // Only GitHub, GitLab, Linear, Jira, and incident.io have provider-specific\n // rules; anything else (a Slack thread, say) is a plain work item, not a\n // mislabeled GitHub issue.\n if (source.integrationId !== 'github') return 'manual';\n return source.type === 'pull-request' ? 'github-pr' : 'github-issue';\n}\n\n// Authored outside the write-access circle: a missing trust stamp fails closed until\n// the reconcile sweep backfills it, and Factory's own PRs pass through `factoryAuthored`.\nexport function externallyAuthored(item: { source: string; metadata: Record<string, unknown> | null }): boolean {\n if (!['github-pr', 'github-issue', 'gitlab-pr', 'gitlab-issue'].includes(item.source)) return false;\n if (item.metadata?.factoryAuthored === true) return false;\n return item.metadata?.authorTrusted !== true;\n}\n\nexport function externallyAuthoredWorkItem(item: {\n externalSource: ExternalWorkItemSource | null;\n metadata: Record<string, unknown> | null;\n}): boolean {\n return externallyAuthored({ source: workItemSource(item.externalSource), metadata: item.metadata });\n}\n\nexport const FACTORY_RULE_STAGES = ['intake', 'triage', 'planning', 'execute', 'review', 'done', 'canceled'] as const;\nexport type FactoryRuleStage = (typeof FACTORY_RULE_STAGES)[number] | (string & {});\n\n// Each role and the working stage its run holds the card in. Key order is the\n// seat pipeline order — Resume depth derives from it.\nexport const FACTORY_ROLE_STAGES = {\n triage: 'triage',\n plan: 'planning',\n work: 'execute',\n review: 'review',\n} as const satisfies Record<string, FactoryRuleStage>;\nexport type FactoryRole = keyof typeof FACTORY_ROLE_STAGES;\n\nexport function isFactoryRole(value: string): value is FactoryRole {\n return value in FACTORY_ROLE_STAGES;\n}\n\nexport const FACTORY_TRIAGE_TYPES = [\n 'bug',\n 'feature request',\n 'docs',\n 'question/support',\n 'maintenance',\n 'duplicate',\n 'resolved',\n 'invalid',\n 'spam',\n 'out-of-scope',\n 'other',\n] as const;\nexport type FactoryTriageType = (typeof FACTORY_TRIAGE_TYPES)[number];\n\nexport function isFactoryTriageType(value: unknown): value is FactoryTriageType {\n return typeof value === 'string' && FACTORY_TRIAGE_TYPES.some(type => type === value);\n}\n\nexport function isFactoryRuleStage(value: unknown): value is FactoryRuleStage {\n return typeof value === 'string' && FACTORY_RULE_STAGES.some(stage => stage === value);\n}\n\nexport function factoryRuleStage(stages: readonly string[]): FactoryRuleStage | undefined {\n const stage = stages.length === 1 ? stages[0] : undefined;\n return typeof stage === 'string' && stage.length > 0 ? stage : undefined;\n}\n\nexport function isTerminalFactoryRuleStage(stages: readonly string[]): boolean {\n const stage = factoryRuleStage(stages);\n return stage === 'done' || stage === 'canceled';\n}\n\n/** Working lanes hold cards with a seat engaged; Intake, Done and Canceled rest them. */\nexport function isWorkingFactoryRuleStage(stage: FactoryRuleStage): boolean {\n return stage !== 'intake' && !isTerminalFactoryRuleStage([stage]);\n}\n\n// Consulted only for the Intake exit: roles don't own lanes, so a card already\n// in a working or terminal lane stays put when a run starts.\nexport function factoryLaneForRole(role: string): FactoryRuleStage | undefined {\n return isFactoryRole(role) ? FACTORY_ROLE_STAGES[role] : undefined;\n}\n\nexport const FACTORY_RULE_BOARDS = ['work', 'review'] as const;\nexport type FactoryRuleBoard = (typeof FACTORY_RULE_BOARDS)[number] | (string & {});\n\nexport const FACTORY_RULE_SOURCES = [\n 'issue',\n 'pullRequest',\n 'gitlabIssue',\n 'gitlabPullRequest',\n 'linearIssue',\n 'jiraIssue',\n 'incidentioFollowUp',\n 'manual',\n] as const;\nexport type FactoryRuleSource = (typeof FACTORY_RULE_SOURCES)[number];\n\nexport const FACTORY_GITHUB_EVENTS = [\n 'issueOpened',\n 'issueEdited',\n 'issueClosed',\n 'issueCommentCreated',\n 'issueCommentEdited',\n 'issueCommentDeleted',\n 'pullRequestOpened',\n 'pullRequestUpdated',\n 'pullRequestCommentCreated',\n 'pullRequestReviewRequested',\n 'pullRequestReviewSubmitted',\n 'pullRequestMerged',\n 'pullRequestClosed',\n] as const;\nexport type FactoryGithubEventName = (typeof FACTORY_GITHUB_EVENTS)[number];\n\nexport const FACTORY_GITLAB_EVENTS = [\n 'issueOpened',\n 'issueUpdated',\n 'issueClosed',\n 'issueCommented',\n 'mergeRequestOpened',\n 'mergeRequestUpdated',\n 'mergeRequestCommented',\n 'mergeRequestMerged',\n 'mergeRequestClosed',\n] as const;\nexport type FactoryGitLabEventName = (typeof FACTORY_GITLAB_EVENTS)[number];\n\nexport const FACTORY_LINEAR_EVENTS = ['issueObserved', 'issueClosed'] as const;\nexport type FactoryLinearEventName = (typeof FACTORY_LINEAR_EVENTS)[number];\n\nexport const FACTORY_JIRA_EVENTS = ['issueObserved', 'issueClosed'] as const;\nexport type FactoryJiraEventName = (typeof FACTORY_JIRA_EVENTS)[number];\n\nexport const FACTORY_INCIDENTIO_EVENTS = ['followUpObserved', 'followUpClosed'] as const;\nexport type FactoryIncidentioEventName = (typeof FACTORY_INCIDENTIO_EVENTS)[number];\n\nexport type FactoryRuleJsonValue =\n | null\n | boolean\n | number\n | string\n | FactoryRuleJsonValue[]\n | { [key: string]: FactoryRuleJsonValue };\n\nexport interface FactoryRuleItemContext {\n id: string;\n source: WorkItemSource;\n sourceKey: string | null;\n parentWorkItemId: string | null;\n title: string;\n url: string | null;\n stages: readonly string[];\n acceptedAt: Date | null;\n /** Intake-stamped facts about the source — repository id, reporter login, labels. */\n metadata: Record<string, unknown> | null;\n}\n\nexport type FactoryRuleActor =\n | { type: 'human'; id: string }\n | { type: 'agent'; bindingId: string; role: string }\n | { type: 'github'; login: string; trusted: boolean; factoryAuthored: boolean }\n | { type: 'gitlab'; username: string; trusted: boolean; factoryAuthored: boolean }\n | { type: 'system'; id: string };\n\nexport interface FactoryRuleIngressIdentity {\n type: 'human' | 'agent' | 'toolResult' | 'github' | 'gitlab' | 'linear' | 'jira' | 'incidentio' | 'rule';\n id: string;\n}\n\nexport interface FactoryRuleCausalEntry {\n ingressId: string;\n decisionType: FactoryCommitDecision['type'];\n}\n\n/** The phase a card sits in, as its board declares it. */\nexport interface FactoryRulePhaseContext {\n id: string;\n kind: 'resting' | 'working' | 'terminal';\n /** Seat carrying a working phase; absent for resting and terminal phases. */\n role?: string;\n /** Outcome names the phase declares, e.g. `approved` or `canceled`; empty when it declares none. */\n outcomes: readonly string[];\n}\n\nexport interface FactoryRuleContextBase {\n tenant: { orgId: string; projectId: string };\n actor: FactoryRuleActor;\n ingress: FactoryRuleIngressIdentity;\n cause: string;\n causalChain: readonly FactoryRuleCausalEntry[];\n configVersion: string;\n}\n\nexport interface FactoryBoundRuleContext extends FactoryRuleContextBase {\n item: FactoryRuleItemContext;\n board: FactoryRuleBoard;\n itemRevision: number;\n}\n\nexport interface FactoryStageRuleContext extends FactoryBoundRuleContext {\n source: FactoryRuleSource;\n stage: FactoryRuleStage;\n fromStage: FactoryRuleStage;\n toStage: FactoryRuleStage;\n /** Project switch: Work items get an independent self-review before the change request opens. Unset means off. */\n selfReviewEnabled?: boolean;\n}\n\nexport interface FactoryToolResultRuleContext extends FactoryBoundRuleContext {\n toolName: string;\n threadId: string;\n assistantMessageId: string;\n toolCallId: string;\n result: {\n status: 'success' | 'error';\n value: FactoryRuleJsonValue;\n };\n}\n\nexport interface FactoryGithubRuleContext extends FactoryRuleContextBase {\n item?: FactoryRuleItemContext;\n board?: FactoryRuleBoard;\n itemRevision?: number;\n /** Where the card's board sends it when its tracked source closes. */\n boardClosure?: FactoryRuleBoardClosure;\n /** What the card's current phase means on its board; absent without a card or when the board does not declare the phase. */\n phase?: FactoryRulePhaseContext;\n /**\n * Board an issue's labels route it to, when the project has a matching label route and that board\n * is installed. Absent for pull requests and for issues whose labels select nothing.\n */\n intake?: FactoryRuleIntakeTarget;\n /**\n * Set on the one evaluation per pull request delivery that files the pull\n * request's own Review card. Opening a pull request concerns two cards — that\n * Review card and the Work item that authored the pull request — so the rule\n * answers for the two separately: the arrival carries this flag and is\n * committed against the authoring item when resolution found one, which is\n * what links the new card to it, while the authoring item's own evaluation\n * leaves the flag unset.\n */\n pullRequestIntake?: boolean;\n event: FactoryGithubEventName;\n deliveryId: string;\n factory: { createdAt: string };\n /** `installationId` is the GitHub App installation the delivery arrived through. */\n repository: { id: number; fullName: string; installationId?: number };\n issue?: {\n number: number;\n title: string;\n url: string;\n createdAt?: string;\n updatedAt?: string;\n assignees?: string[];\n labels?: string[];\n state?: 'open' | 'closed';\n /** GitHub close reason: `completed`, `not_planned`, or `duplicate`. */\n stateReason?: string;\n };\n issueChange?: { title: boolean; body: boolean };\n issueComment?: {\n id: number;\n body?: string;\n url?: string;\n author?: string;\n authorType?: string;\n createdAt?: string;\n updatedAt?: string;\n };\n pullRequest?: {\n number: number;\n title: string;\n url: string;\n createdAt?: string;\n state: 'open' | 'closed';\n draft: boolean;\n merged: boolean;\n assignees?: string[];\n requestedReviewers?: string[];\n labels?: string[];\n author?: string;\n factoryAuthored: boolean;\n headBranch: string;\n baseBranch: string;\n };\n /** Present on `pullRequestReviewRequested`: who review was (re-)requested from. */\n reviewRequest?: { reviewer: string; factoryReviewer: boolean };\n /** Present when a PR comment uses Factory's exact review command. */\n reviewCommand?: { command: 'review' | 're-review'; target: string };\n /**\n * Present on `pullRequestReviewSubmitted`: the review that was just posted, or a\n * request-changes review whose body was edited to a new Factory verdict.\n */\n review?: { id: number; state: string; url: string; author?: string; body?: string };\n}\n\n/**\n * Where an intake source binding says new items from this source should land.\n * Absent when the source is unbound or bound without a board (built-in routing).\n */\nexport interface FactoryRuleIntakeTarget {\n board: string;\n initialPhase: string;\n}\n\n/**\n * Where a source-closed rule moves the card, from the `sourceClosed` mapping its installed board\n * declares. Absent when the card's board is not installed or declares no mapping.\n */\nexport interface FactoryRuleBoardClosure {\n /** Title of the card's board, for messages. */\n boardTitle: string;\n /** Terminal phase for a source that was completed. */\n completed: FactoryRuleBoardClosurePhase;\n /** Terminal phase for a source that was canceled or not planned. */\n canceled: FactoryRuleBoardClosurePhase;\n /** Whether the card already sits in a terminal phase of its board. */\n itemTerminal: boolean;\n}\n\nexport interface FactoryRuleBoardClosurePhase {\n phase: string;\n title: string;\n}\n\nexport interface FactoryGitLabRuleContext extends FactoryRuleContextBase {\n item?: FactoryRuleItemContext;\n board?: FactoryRuleBoard;\n itemRevision?: number;\n /** Where the card's board sends it when its tracked source closes. */\n boardClosure?: FactoryRuleBoardClosure;\n /** What the card's current phase means on its board; absent without a card or when the board does not declare the phase. */\n phase?: FactoryRulePhaseContext;\n intake?: FactoryRuleIntakeTarget;\n event: FactoryGitLabEventName;\n deliveryId: string;\n factory: { createdAt: string };\n repository: { id: number; fullName: string; host: string };\n issue?: {\n number: number;\n sourceKey: string;\n title: string;\n url: string;\n createdAt?: string;\n updatedAt?: string;\n assignees?: string[];\n labels?: string[];\n labelColors?: Record<string, string>;\n state?: 'open' | 'closed';\n author?: string;\n authorTrusted: boolean;\n };\n note?: {\n id: number;\n body?: string;\n url?: string;\n author?: string;\n createdAt?: string;\n updatedAt?: string;\n };\n mergeRequest?: {\n number: number;\n sourceKey: string;\n title: string;\n url: string;\n createdAt?: string;\n state: 'open' | 'closed';\n draft: boolean;\n merged: boolean;\n assignees?: string[];\n reviewers?: string[];\n labels?: string[];\n labelColors?: Record<string, string>;\n author?: string;\n authorTrusted: boolean;\n factoryAuthored: boolean;\n headBranch: string;\n baseBranch: string;\n };\n}\n\nexport interface FactoryLinearRuleContext extends FactoryRuleContextBase {\n item?: FactoryRuleItemContext;\n board?: FactoryRuleBoard;\n itemRevision?: number;\n /** Where the card's board sends it when its tracked source closes. */\n boardClosure?: FactoryRuleBoardClosure;\n /** What the card's current phase means on its board; absent without a card or when the board does not declare the phase. */\n phase?: FactoryRulePhaseContext;\n /** Bound board for the source this issue came from, when one is configured and installed. */\n intake?: FactoryRuleIntakeTarget;\n event: FactoryLinearEventName;\n issue: {\n id: string;\n identifier: string;\n title: string;\n url: string;\n state: string;\n stateType: string;\n priorityLabel: string;\n assignee: string | null;\n creator: string | null;\n team: string | null;\n labels: readonly string[];\n createdAt: string;\n updatedAt: string;\n sourceId?: string | null;\n projectId?: string | null;\n };\n}\n\nexport interface FactoryJiraRuleContext extends FactoryRuleContextBase {\n item?: FactoryRuleItemContext;\n board?: FactoryRuleBoard;\n itemRevision?: number;\n /** Where the card's board sends it when its tracked source closes. */\n boardClosure?: FactoryRuleBoardClosure;\n /** What the card's current phase means on its board; absent without a card or when the board does not declare the phase. */\n phase?: FactoryRulePhaseContext;\n /** Bound board for the source this issue came from, when one is configured and installed. */\n intake?: FactoryRuleIntakeTarget;\n event: FactoryJiraEventName;\n issue: {\n /** Stable issue reference — the direct integration's Jira id or the Platform-encoded issue reference. */\n id: string;\n identifier: string;\n title: string;\n url: string;\n state: string;\n stateType: string;\n priorityLabel: string;\n assignee: string | null;\n author: string | null;\n project: string | null;\n site: string | null;\n labels: readonly string[];\n createdAt: string;\n updatedAt: string;\n };\n}\n\nexport interface FactoryIncidentioRuleContext extends FactoryRuleContextBase {\n item?: FactoryRuleItemContext;\n board?: FactoryRuleBoard;\n itemRevision?: number;\n /** Where the card's board sends it when its tracked source closes. */\n boardClosure?: FactoryRuleBoardClosure;\n /** What the card's current phase means on its board; absent without a card or when the board does not declare the phase. */\n phase?: FactoryRulePhaseContext;\n /** Bound board for the source this follow-up came from, when one is configured and installed. */\n intake?: FactoryRuleIntakeTarget;\n event: FactoryIncidentioEventName;\n issue: {\n /** Stable item reference — the prefixed incident.io follow-up id the intake feed serves as `id`. */\n id: string;\n identifier: string;\n title: string;\n url: string;\n state: string;\n stateType: string;\n priorityLabel: string;\n assignee: string | null;\n author: string | null;\n /** Reference of the incident this follow-up belongs to, when available. */\n incident: string | null;\n labels: readonly string[];\n createdAt: string;\n updatedAt: string;\n };\n}\n\nexport type FactoryRuleHandler<TContext> = (\n context: Readonly<TContext>,\n) => FactoryRuleDecision | void | Promise<FactoryRuleDecision | void>;\n\nexport interface FactoryBoardRuleLeaf {\n onEnter?: FactoryRuleHandler<FactoryStageRuleContext>;\n onExit?: FactoryRuleHandler<FactoryStageRuleContext>;\n}\n\nexport type FactoryRuleRejectionCode =\n | 'forbidden'\n | 'invalid_transition'\n | 'missing_binding'\n | 'stale'\n | 'timeout'\n | 'rule_error'\n | 'causal_depth_exceeded'\n | 'repeated_transition'\n | 'approval_required';\n\nexport interface FactoryRuleRejectDecision {\n type: 'reject';\n code: FactoryRuleRejectionCode;\n reason: string;\n}\n\ninterface FactoryCommitDecisionBase {\n idempotencyKey: string;\n}\n\nexport interface FactoryTransitionDecision extends FactoryCommitDecisionBase {\n type: 'transition';\n board: FactoryRuleBoard;\n stage: FactoryRuleStage;\n /**\n * Delivered to the item's active session (waking it if idle) after the\n * transition commits. Skipped when the item has no active run binding, so\n * informational messages never fail the transition.\n */\n message?: { text: string; role?: string };\n /**\n * Runs the stage's entry rules even when the item is already in that stage.\n * A transition to the current stage is normally inert, because most callers\n * are correcting a board into a state it already holds. Re-entry is for the\n * opposite case: the stage's work is in flight and has been invalidated, so\n * it has to start over.\n */\n reenter?: boolean;\n}\n\nexport interface FactoryUpsertLinkedWorkItemDecision extends FactoryCommitDecisionBase {\n type: 'upsertLinkedWorkItem';\n board: FactoryRuleBoard;\n source: WorkItemSource;\n sourceKey: string;\n /**\n * Org-wide ownership key for the external record, when one Factory at a time\n * may hold a live card for it (a stable Linear issue id, for instance). The\n * store enforces it with a unique index, so a second project's materialization\n * is refused rather than duplicated.\n */\n claimKey?: string;\n title: string;\n url: string | null;\n stage: FactoryRuleStage;\n /**\n * File the card at `stage` as its first entry and run none of the board's\n * phase rules for it — no arrival, no destination entry. The card is filed\n * (or, if it already exists, moved) and left parked for a person; nothing is\n * started for it. For external records that arrive already past the board's\n * first step: a GitHub issue whose triage is already recorded, for instance.\n *\n * Placement is the whole decision. An existing card it reaches is moved to\n * that stage through the same relocation the label routes use, and its\n * metadata is left alone.\n */\n skipRules?: boolean;\n metadata?: Record<string, FactoryRuleJsonValue>;\n}\n\ninterface FactoryInvokeSkillDecisionBase extends FactoryCommitDecisionBase {\n type: 'invokeSkill';\n role: string;\n arguments?: string;\n precedingMessage?: string;\n cancelInFlight?: boolean;\n}\n\n/**\n * Starting an agent run. Most runs activate a skill, because the skill carries\n * the handoff contract later rules match on. A run whose completion is already\n * signalled some other way — Building finishes by opening a pull request, which\n * arrives as its own event — needs no contract, so it can carry a plain prompt\n * instead of an otherwise empty skill.\n */\nexport type FactoryInvokeSkillDecision = FactoryInvokeSkillDecisionBase &\n (\n | {\n skillName: string;\n prompt?: never;\n /**\n * Same-stage re-entry: the skill is already active in the card's live session,\n * so deliver a compact continuation that references it by name and carries only\n * the fresh arguments, instead of re-pasting the whole skill document. Only valid\n * for named-skill decisions — a plain prompt run has no active skill to resume.\n */\n resume?: boolean;\n }\n | { prompt: string; skillName?: never; resume?: never }\n );\n\nexport interface FactorySendMessageDecision extends FactoryCommitDecisionBase {\n type: 'sendMessage';\n /** Omitted: the card's live session, whichever seat holds it. Required with `prepareBinding`. */\n role?: string;\n message: string;\n priority?: 'medium' | 'high' | 'urgent';\n idleBehavior?: 'persist' | 'wake';\n prepareBinding?: boolean;\n}\n\nexport interface FactoryNotifyDecision extends FactoryCommitDecisionBase {\n type: 'notify';\n title: string;\n body?: string;\n level?: 'info' | 'warning' | 'error';\n}\n\n/**\n * Dismisses Factory change requests an approval has superseded on a GitHub\n * pull request. Only reviews carrying Factory's `Verdict: request changes`\n * marker from an identity other than the approving one are dismissed.\n */\nexport interface FactoryDismissStaleReviewsDecision extends FactoryCommitDecisionBase {\n type: 'dismissStaleReviews';\n installationId: number;\n repository: string;\n pullRequestNumber: number;\n approvingReviewId: string;\n approvingAuthor: string;\n}\n\nexport type FactoryCommitDecision =\n | FactoryTransitionDecision\n | FactoryDismissStaleReviewsDecision\n | FactoryUpsertLinkedWorkItemDecision\n | FactoryInvokeSkillDecision\n | FactorySendMessageDecision\n | FactoryNotifyDecision;\n\nexport type FactoryRuleDecision = FactoryRuleRejectDecision | FactoryCommitDecision;\n\nexport interface FactoryTransitionResultAccepted {\n status: 'accepted';\n transitionId: string;\n itemId: string;\n revision: number;\n stage: FactoryRuleStage;\n decisions: FactoryCommitDecision[];\n}\n\nexport interface FactoryTransitionResultRejected {\n status: 'rejected';\n transitionId: string;\n itemId: string;\n code: FactoryRuleRejectionCode;\n reason: string;\n}\n\nexport type FactoryTransitionResult = FactoryTransitionResultAccepted | FactoryTransitionResultRejected;\n\nexport function factoryRuleSourceForWorkItem(source: WorkItemSource): FactoryRuleSource {\n switch (source) {\n case 'github-issue':\n return 'issue';\n case 'github-pr':\n return 'pullRequest';\n case 'gitlab-issue':\n return 'gitlabIssue';\n case 'gitlab-pr':\n return 'gitlabPullRequest';\n case 'linear-issue':\n return 'linearIssue';\n case 'jira-issue':\n return 'jiraIssue';\n case 'incidentio-follow-up':\n return 'incidentioFollowUp';\n case 'manual':\n return 'manual';\n }\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- //#region src/rules/validation.ts
1
+ // src/rules/validation.ts
2
2
  const MAX_FACTORY_RULE_CAUSAL_DEPTH = 8;
3
3
  const MAX_VERSION_LENGTH = 128;
4
4
  const MAX_IDEMPOTENCY_KEY_LENGTH = 256;
@@ -321,7 +321,6 @@ function validateFactoryRuleDecisions(values, causalDepth = 0) {
321
321
  if (new Set(keys).size !== keys.length) throw new FactoryRuleValidationError("Factory decisions require unique idempotency keys.");
322
322
  return decisions;
323
323
  }
324
- //#endregion
325
324
  export { BOARD_IDENTIFIER_RE, DEFAULT_FACTORY_CONFIG_VERSION, FactoryRuleValidationError, IDENTIFIER_RE, MAX_BOARD_IDENTIFIER_LENGTH, MAX_FACTORY_RULE_CAUSAL_DEPTH, MAX_ROLE_LENGTH, MAX_TOOL_NAME_LENGTH, assertFactoryConfigVersion, assertFactoryDecisionTarget, isBoardIdentifier, normalizeFactoryRuleJsonValue, validateFactoryRuleDecision, validateFactoryRuleDecisions };
326
325
 
327
326
  //# sourceMappingURL=validation.js.map
@@ -1,5 +1,5 @@
1
1
  import { FactorySandbox } from "@mastra/core/workspace";
2
- //#region src/sandbox/callback-factory-sandbox.ts
2
+ // src/sandbox/callback-factory-sandbox.ts
3
3
  /**
4
4
  * Wraps the `sandbox: ctx => new SomeSandbox(...)` host callback
5
5
  * as a FactorySandbox. The host owns everything inside the callback, so the
@@ -16,7 +16,6 @@ var CallbackFactorySandbox = class extends FactorySandbox {
16
16
  return this.#callback(ctx);
17
17
  }
18
18
  };
19
- //#endregion
20
19
  export { CallbackFactorySandbox };
21
20
 
22
21
  //# sourceMappingURL=callback-factory-sandbox.js.map
@@ -1,4 +1,4 @@
1
- //#region src/sandbox/git-ref.ts
1
+ // src/sandbox/git-ref.ts
2
2
  /**
3
3
  * Validate a branch/ref name before persistence or use in a Git command.
4
4
  * The conservative character allowlist is paired with Git's structural rules
@@ -9,7 +9,6 @@ function isValidGitRef(value) {
9
9
  if (value.startsWith("-") || value.startsWith("/") || value.endsWith("/") || value.endsWith(".") || value.includes("..") || value.includes("//")) return false;
10
10
  return value.split("/").every((component) => !component.startsWith(".") && !component.toLowerCase().endsWith(".lock"));
11
11
  }
12
- //#endregion
13
12
  export { isValidGitRef };
14
13
 
15
14
  //# sourceMappingURL=git-ref.js.map
@@ -1,9 +1,17 @@
1
1
  import { FactorySandbox, LocalSandbox } from '@mastra/core/workspace';
2
2
  import type { FactorySandboxContext } from '@mastra/core/workspace';
3
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. */
4
+ /**
5
+ * Directory under which every session gets its own working directory.
6
+ * Defaults to `MASTRACODE_LOCAL_SANDBOX_ROOT`, else `~/.mastracode/web/sandboxes`.
7
+ */
8
+ root?: string;
9
+ /**
10
+ * Environment for commands run in the sandbox. Defaults to an allow-list of
11
+ * host variables that keep git and TLS working (HOME, USER, SHELL, locale,
12
+ * SSL_CERT_*, …), so app secrets never reach commands run against untrusted
13
+ * repository checkouts. PATH is always added by LocalSandbox itself.
14
+ */
7
15
  env?: Record<string, string>;
8
16
  }
9
17
  /**
@@ -14,7 +22,7 @@ export interface LocalFactorySandboxOptions {
14
22
  export declare class LocalFactorySandbox extends FactorySandbox<Record<string, never>> {
15
23
  #private;
16
24
  readonly provider = "local";
17
- constructor(options: LocalFactorySandboxOptions);
25
+ constructor(options?: LocalFactorySandboxOptions);
18
26
  create(ctx: FactorySandboxContext): LocalSandbox;
19
27
  }
20
28
  //# sourceMappingURL=local-factory-sandbox.d.ts.map
@@ -1 +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;CAQjD"}
1
+ {"version":3,"file":"local-factory-sandbox.d.ts","sourceRoot":"","sources":["../../src/sandbox/local-factory-sandbox.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAEpE,MAAM,WAAW,0BAA0B;IACzC;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9B;AA2BD;;;;GAIG;AACH,qBAAa,mBAAoB,SAAQ,cAAc,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;;IAC5E,QAAQ,CAAC,QAAQ,WAAW;gBAKhB,OAAO,GAAE,0BAA+B;IAQpD,MAAM,CAAC,GAAG,EAAE,qBAAqB,GAAG,YAAY;CAQjD"}
@@ -1,6 +1,30 @@
1
1
  import { FactorySandbox, LocalSandbox } from "@mastra/core/workspace";
2
2
  import { join } from "path";
3
- //#region src/sandbox/local-factory-sandbox.ts
3
+ import { homedir } from "os";
4
+ // src/sandbox/local-factory-sandbox.ts
5
+ const HOST_ENV_KEYS = [
6
+ "HOME",
7
+ "USER",
8
+ "LOGNAME",
9
+ "SHELL",
10
+ "TMPDIR",
11
+ "LANG",
12
+ "LC_ALL",
13
+ "TERM",
14
+ "TZ",
15
+ "GIT_EXEC_PATH",
16
+ "GIT_TEMPLATE_DIR",
17
+ "SSL_CERT_FILE",
18
+ "SSL_CERT_DIR"
19
+ ];
20
+ function hostEnv() {
21
+ const env = {};
22
+ for (const key of HOST_ENV_KEYS) {
23
+ const value = process.env[key];
24
+ if (value) env[key] = value;
25
+ }
26
+ return env;
27
+ }
4
28
  /**
5
29
  * Sessions run on the host machine: each session's sandbox is a LocalSandbox
6
30
  * rooted at `join(root, sessionId)`, where the repository checks out as a
@@ -8,21 +32,22 @@ import { join } from "path";
8
32
  */
9
33
  var LocalFactorySandbox = class extends FactorySandbox {
10
34
  provider = "local";
11
- #options;
12
- constructor(options) {
35
+ #root;
36
+ #env;
37
+ constructor(options = {}) {
13
38
  super();
14
- this.#options = options;
39
+ this.#root = options.root ?? (process.env.MASTRACODE_LOCAL_SANDBOX_ROOT?.trim() || join(homedir(), ".mastracode", "web", "sandboxes"));
40
+ this.#env = options.env ?? hostEnv();
15
41
  }
16
42
  create(ctx) {
17
43
  return new LocalSandbox({
18
44
  id: ctx.sessionId,
19
45
  timeout: 3e4,
20
- workingDirectory: join(this.#options.root, ctx.sessionId),
21
- ...this.#options.env ? { env: this.#options.env } : {}
46
+ workingDirectory: join(this.#root, ctx.sessionId),
47
+ env: this.#env
22
48
  });
23
49
  }
24
50
  };
25
- //#endregion
26
51
  export { LocalFactorySandbox };
27
52
 
28
53
  //# sourceMappingURL=local-factory-sandbox.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"local-factory-sandbox.js","names":[],"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 timeout: 30_000,\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,KAAK,WAAW;CAClB;CAEA,OAAO,KAA0C;EAC/C,OAAO,IAAI,aAAa;GACtB,IAAI,IAAI;GACR,SAAS;GACT,kBAAkB,KAAK,KAAK,SAAS,MAAM,IAAI,SAAS;GACxD,GAAI,KAAK,SAAS,MAAM,EAAE,KAAK,KAAK,SAAS,IAAI,IAAI,CAAC;EACxD,CAAC;CACH;AACF"}
1
+ {"version":3,"file":"local-factory-sandbox.js","names":[],"sources":["../../src/sandbox/local-factory-sandbox.ts"],"sourcesContent":["import { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { FactorySandbox, LocalSandbox } from '@mastra/core/workspace';\nimport type { FactorySandboxContext } from '@mastra/core/workspace';\n\nexport interface LocalFactorySandboxOptions {\n /**\n * Directory under which every session gets its own working directory.\n * Defaults to `MASTRACODE_LOCAL_SANDBOX_ROOT`, else `~/.mastracode/web/sandboxes`.\n */\n root?: string;\n /**\n * Environment for commands run in the sandbox. Defaults to an allow-list of\n * host variables that keep git and TLS working (HOME, USER, SHELL, locale,\n * SSL_CERT_*, …), so app secrets never reach commands run against untrusted\n * repository checkouts. PATH is always added by LocalSandbox itself.\n */\n env?: Record<string, string>;\n}\n\nconst HOST_ENV_KEYS = [\n 'HOME',\n 'USER',\n 'LOGNAME',\n 'SHELL',\n 'TMPDIR',\n 'LANG',\n 'LC_ALL',\n 'TERM',\n 'TZ',\n 'GIT_EXEC_PATH',\n 'GIT_TEMPLATE_DIR',\n 'SSL_CERT_FILE',\n 'SSL_CERT_DIR',\n] as const;\n\nfunction hostEnv(): Record<string, string> {\n const env: Record<string, string> = {};\n for (const key of HOST_ENV_KEYS) {\n const value = process.env[key];\n if (value) env[key] = value;\n }\n return env;\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 #root: string;\n readonly #env: Record<string, string>;\n\n constructor(options: LocalFactorySandboxOptions = {}) {\n super();\n this.#root =\n options.root ??\n (process.env.MASTRACODE_LOCAL_SANDBOX_ROOT?.trim() || join(homedir(), '.mastracode', 'web', 'sandboxes'));\n this.#env = options.env ?? hostEnv();\n }\n\n create(ctx: FactorySandboxContext): LocalSandbox {\n return new LocalSandbox({\n id: ctx.sessionId,\n timeout: 30_000,\n workingDirectory: join(this.#root, ctx.sessionId),\n env: this.#env,\n });\n }\n}\n"],"mappings":";;;;AAoBA,MAAM,gBAAgB;CACpB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAEA,SAAS,UAAkC;CACzC,MAAM,MAA8B,CAAC;CACrC,KAAK,MAAM,OAAO,eAAe;EAC/B,MAAM,QAAQ,QAAQ,IAAI;EAC1B,IAAI,OAAO,IAAI,OAAO;CACxB;CACA,OAAO;AACT;;;;;;AAOA,IAAa,sBAAb,cAAyC,eAAsC;CAC7E,WAAoB;CAEpB;CACA;CAEA,YAAY,UAAsC,CAAC,GAAG;EACpD,MAAM;EACN,KAAK,QACH,QAAQ,SACP,QAAQ,IAAI,+BAA+B,KAAK,KAAK,KAAK,QAAQ,GAAG,eAAe,OAAO,WAAW;EACzG,KAAK,OAAO,QAAQ,OAAO,QAAQ;CACrC;CAEA,OAAO,KAA0C;EAC/C,OAAO,IAAI,aAAa;GACtB,IAAI,IAAI;GACR,SAAS;GACT,kBAAkB,KAAK,KAAK,OAAO,IAAI,SAAS;GAChD,KAAK,KAAK;EACZ,CAAC;CACH;AACF"}
@@ -1,4 +1,4 @@
1
- //#region src/sandbox/materialization.ts
1
+ // src/sandbox/materialization.ts
2
2
  /**
3
3
  * Narrow a sandbox to one that can run commands, or fail saying which
4
4
  * capability is missing.
@@ -12,7 +12,6 @@ function requireExec(sandbox) {
12
12
  if (typeof sandbox.executeCommand !== "function") throw new Error(`Sandbox provider '${sandbox.provider}' does not support executeCommand, which is required to run git and filesystem operations in a session.`);
13
13
  return sandbox;
14
14
  }
15
- //#endregion
16
15
  export { requireExec };
17
16
 
18
17
  //# sourceMappingURL=materialization.js.map
@@ -3,7 +3,7 @@ import { requireExec } from "./materialization.js";
3
3
  import { clearFailedSetupCommands, evictSessionSandbox, peekSessionSandbox } from "./session-sandbox.js";
4
4
  import { peekSessionEnvironmentTeardown } from "../session/environment-state-processor.js";
5
5
  import { destroyDetachedSessionSandbox, releaseSessionSandbox } from "../integrations/github/sandbox-release.js";
6
- //#region src/sandbox/session-retirement.ts
6
+ // src/sandbox/session-retirement.ts
7
7
  const DEFAULT_DESTROY_TIMEOUT_MS = 3e4;
8
8
  /**
9
9
  * A provider error meaning the VM is already gone: 410, or a 404 the proxy
@@ -322,7 +322,6 @@ var SessionRetirementCoordinator = class {
322
322
  }
323
323
  }
324
324
  };
325
- //#endregion
326
325
  export { SessionRetirementCoordinator };
327
326
 
328
327
  //# sourceMappingURL=session-retirement.js.map
@@ -2,7 +2,7 @@ import { timedPhase } from "../timing.js";
2
2
  import { SETUP_FAILED_MARKER_PATH, SETUP_MARKER_PATH, WORKSPACE_SETUP_MARKER_PATH, repoSetupMarkerPath, setupMarkerContent } from "../packages/_internals/workspace/dist/index.js";
3
3
  import { deriveLocalWorkdir, deriveRemoteRepoDir, repoDirUnder, repositoryDirectoryName } from "./workdir.js";
4
4
  import path from "path";
5
- //#region src/sandbox/session-sandbox.ts
5
+ // src/sandbox/session-sandbox.ts
6
6
  const sessionSandboxes = /* @__PURE__ */ new Map();
7
7
  /** The layout of a memoized session, `undefined` until its workdir resolved. */
8
8
  function sessionLayout(entry) {
@@ -246,7 +246,6 @@ function createEnvironmentSetupHook(run, sessionId, repoFullName, environment) {
246
246
  });
247
247
  };
248
248
  }
249
- //#endregion
250
249
  export { __clearSessionSandboxesForTests, clearFailedSetupCommands, createEnvironmentSetupHook, createSessionSetupHook, evictSessionSandbox, getSessionSandbox, hasFailedSetupCommand, peekSessionSandbox, recordFailedSetupCommand, resolveSessionWorkdir, sessionLayout };
251
250
 
252
251
  //# sourceMappingURL=session-sandbox.js.map
@@ -1,5 +1,5 @@
1
1
  import * as path$1 from "path";
2
- //#region src/sandbox/workdir.ts
2
+ // src/sandbox/workdir.ts
3
3
  /**
4
4
  * Session workdir derivation. Workdirs are never persisted or trusted from
5
5
  * storage or client input — the stale-workdir class of production bugs came
@@ -58,7 +58,6 @@ function resolveContainedLocalWorkdir(root, ...segments) {
58
58
  if (resolved !== resolvedRoot && resolved.startsWith(`${resolvedRoot}${path$1.sep}`)) return resolved;
59
59
  throw new Error(`Refusing to use local sandbox path outside configured root: ${resolved}`);
60
60
  }
61
- //#endregion
62
61
  export { deriveLocalWorkdir, deriveRemoteRepoDir, repoDirUnder, repositoryDirectoryName, resolveContainedLocalWorkdir, sanitizeSegment };
63
62
 
64
63
  //# sourceMappingURL=workdir.js.map
@@ -1,5 +1,5 @@
1
1
  import { createCipheriv, createDecipheriv } from "crypto";
2
- //#region src/secret-encryption.ts
2
+ // src/secret-encryption.ts
3
3
  const ENVELOPE_PREFIX = "mastra:factory-secret:v1:";
4
4
  const ALGORITHM = "aes-256-gcm";
5
5
  const IV_BYTES = 12;
@@ -141,7 +141,6 @@ function createPlaintextFactorySecretEncryption() {
141
141
  }
142
142
  };
143
143
  }
144
- //#endregion
145
144
  export { createFactorySecretEncryption, createPlaintextFactorySecretEncryption };
146
145
 
147
146
  //# sourceMappingURL=secret-encryption.js.map
@@ -1,5 +1,5 @@
1
1
  import { HTTPException } from "hono/http-exception";
2
- //#region src/server-error.ts
2
+ // src/server-error.ts
3
3
  /** Handle a route error: log full detail server-side, return structured JSON. */
4
4
  function handleServerError(err, c) {
5
5
  if (err instanceof HTTPException) return c.json({ error: err.message }, err.status);
@@ -10,7 +10,6 @@ function handleServerError(err, c) {
10
10
  message: err instanceof Error ? err.message : String(err)
11
11
  }, 500);
12
12
  }
13
- //#endregion
14
13
  export { handleServerError };
15
14
 
16
15
  //# sourceMappingURL=server-error.js.map
@@ -2,7 +2,7 @@ import { tenantOrgId } from "../routes/provider-credentials.js";
2
2
  import { effectiveModelScope } from "../storage/domains/projects/base.js";
3
3
  import { UUID_PATTERN } from "../routes/contracts.js";
4
4
  import { resolveAuthenticatedCredentialTenant } from "@mastra/code-sdk/agents/credential-resolver";
5
- //#region src/session/credential-policy.ts
5
+ // src/session/credential-policy.ts
6
6
  const FACTORY_PROJECT_ID = new RegExp(UUID_PATTERN);
7
7
  /** Resolve billing authority from authenticated identity and durable bindings, never session state. */
8
8
  async function resolveFactoryCredentialTenant(requestContext, deps) {
@@ -60,7 +60,6 @@ async function routedPersonalDefaults(tenant, modelDefaults) {
60
60
  userId: tenant.userId
61
61
  });
62
62
  }
63
- //#endregion
64
63
  export { resolveFactoryCredentialTenant, resolveFactoryModelPolicy, routedPersonalDefaults };
65
64
 
66
65
  //# sourceMappingURL=credential-policy.js.map
@@ -1,6 +1,6 @@
1
1
  import { primeTenantCredentialsForTenant, resolveTenantProviderDefaultModel } from "../routes/tenant-credentials.js";
2
2
  import { effectiveModelScope } from "../storage/domains/projects/base.js";
3
- //#region src/session/default-model-hydration.ts
3
+ // src/session/default-model-hydration.ts
4
4
  async function applyDefaultModel(session, modelId) {
5
5
  await session.model.switch(modelId);
6
6
  for (const agentType of [
@@ -86,7 +86,6 @@ async function hydrateSessionDefaultModel(session, { sourceControl, projects, wo
86
86
  console.warn("[Factory default-model hydration] Unable to apply the scoped default model.", error);
87
87
  }
88
88
  }
89
- //#endregion
90
89
  export { hydrateSessionDefaultModel };
91
90
 
92
91
  //# sourceMappingURL=default-model-hydration.js.map
@@ -1,4 +1,4 @@
1
- //#region src/session/environment-repositories.ts
1
+ // src/session/environment-repositories.ts
2
2
  /**
3
3
  * The `inEnvironment` links of a factory in position order, resolved to their
4
4
  * repository and connection rows, for the integration the handle belongs to.
@@ -83,7 +83,6 @@ function findEnvironmentRepository(repositories, slug) {
83
83
  function environmentSlugs(repositories) {
84
84
  return repositories.map((candidate) => candidate.repository.slug);
85
85
  }
86
- //#endregion
87
86
  export { environmentSlugs, findEnvironmentRepository, resolveEnvironmentRepositories, resolveSessionRepositories };
88
87
 
89
88
  //# sourceMappingURL=environment-repositories.js.map
@@ -8,7 +8,6 @@ export interface SessionEnvironmentRepositoryState {
8
8
  /** The branch the checkout ended on after boot; `null` when it stays detached. */
9
9
  branch: string | null;
10
10
  defaultBranch: string;
11
- position: number;
12
11
  /** `ok` ran or was already set up, `failed` exited non-zero, `skipped` has no setup command. */
13
12
  setupStatus: EnvironmentRepositorySetupStatus;
14
13
  /** The change request this session opened in the repository, once it has. */
@@ -1 +1 @@
1
- {"version":3,"file":"environment-state-processor.d.ts","sourceRoot":"","sources":["../../src/session/environment-state-processor.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,sBAAsB,EAAE,wBAAwB,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAK3G,MAAM,MAAM,gCAAgC,GAAG,IAAI,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE3E,4GAA4G;AAC5G,MAAM,WAAW,iCAAiC;IAChD,IAAI,EAAE,MAAM,CAAC;IACb,mEAAmE;IACnE,GAAG,EAAE,MAAM,CAAC;IACZ,kFAAkF;IAClF,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,gGAAgG;IAChG,WAAW,EAAE,gCAAgC,CAAC;IAC9C,6EAA6E;IAC7E,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,uBAAuB;IACtC,wFAAwF;IACxF,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,iCAAiC,EAAE,CAAC;IAClD,+FAA+F;IAC/F,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAQD,2GAA2G;AAC3G,MAAM,WAAW,0BAA0B;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,wBAAwB,CACtC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,uBAAuB,EAC9B,QAAQ,GAAE,0BAA0B,EAAO,GAC1C,IAAI,CAEN;AAED;;;;;GAKG;AACH,wBAAgB,kCAAkC,CAChD,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAAE,GACpD,IAAI,CAaN;AAED,wGAAwG;AACxG,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAKtF;AAED,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAE/D;AAED,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,uBAAuB,GAAG,SAAS,CAE7F;AAED,sEAAsE;AACtE,wBAAgB,8BAA8B,CAAC,SAAS,EAAE,MAAM,GAAG,0BAA0B,EAAE,CAE9F;AAED,wBAAgB,kCAAkC,IAAI,IAAI,CAEzD;AAmBD;;;;;GAKG;AACH,qBAAa,gCAAiC,YAAW,SAAS,CAAC,qBAAqB,CAAC;IACvF,QAAQ,CAAC,EAAE,yBAAY;IACvB,QAAQ,CAAC,OAAO,yBAAY;IAEtB,kBAAkB,CAAC,IAAI,EAAE,sBAAsB,GAAG,OAAO,CAAC,wBAAwB,CAAC;CAiC1F"}
1
+ {"version":3,"file":"environment-state-processor.d.ts","sourceRoot":"","sources":["../../src/session/environment-state-processor.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,sBAAsB,EAAE,wBAAwB,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAK3G,MAAM,MAAM,gCAAgC,GAAG,IAAI,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE3E,4GAA4G;AAC5G,MAAM,WAAW,iCAAiC;IAChD,IAAI,EAAE,MAAM,CAAC;IACb,mEAAmE;IACnE,GAAG,EAAE,MAAM,CAAC;IACZ,kFAAkF;IAClF,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,gGAAgG;IAChG,WAAW,EAAE,gCAAgC,CAAC;IAC9C,6EAA6E;IAC7E,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,uBAAuB;IACtC,wFAAwF;IACxF,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,iCAAiC,EAAE,CAAC;IAClD,+FAA+F;IAC/F,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAQD,2GAA2G;AAC3G,MAAM,WAAW,0BAA0B;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,wBAAwB,CACtC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,uBAAuB,EAC9B,QAAQ,GAAE,0BAA0B,EAAO,GAC1C,IAAI,CAEN;AAED;;;;;GAKG;AACH,wBAAgB,kCAAkC,CAChD,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAAE,GACpD,IAAI,CAaN;AAED,wGAAwG;AACxG,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAKtF;AAED,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAE/D;AAED,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,uBAAuB,GAAG,SAAS,CAE7F;AAED,sEAAsE;AACtE,wBAAgB,8BAA8B,CAAC,SAAS,EAAE,MAAM,GAAG,0BAA0B,EAAE,CAE9F;AAED,wBAAgB,kCAAkC,IAAI,IAAI,CAEzD;AAmBD;;;;;GAKG;AACH,qBAAa,gCAAiC,YAAW,SAAS,CAAC,qBAAqB,CAAC;IACvF,QAAQ,CAAC,EAAE,yBAAY;IACvB,QAAQ,CAAC,OAAO,yBAAY;IAEtB,kBAAkB,CAAC,IAAI,EAAE,sBAAsB,GAAG,OAAO,CAAC,wBAAwB,CAAC;CAiC1F"}
@@ -1,5 +1,5 @@
1
1
  import { createHash } from "crypto";
2
- //#region src/session/environment-state-processor.ts
2
+ // src/session/environment-state-processor.ts
3
3
  const STATE_ID = "factory-environment";
4
4
  const environments = /* @__PURE__ */ new Map();
5
5
  function recordSessionEnvironment(sessionId, state, teardown = []) {
@@ -81,7 +81,7 @@ var FactoryEnvironmentStateProcessor = class {
81
81
  if (!state) return;
82
82
  const cacheKey = environmentCacheKey(state);
83
83
  if (Boolean(args.lastSnapshot) && args.contextWindow.hasSnapshot && (args.tracking?.currentCacheKey ?? args.lastSnapshot?.metadata?.state?.cacheKey) === cacheKey) return;
84
- const lines = state.repositories.map((repo) => `${repo.position}. ${escapeText(repo.slug)} at ${escapeText(repo.dir)} on ${escapeText(repo.branch ?? "(detached)")} (default ${escapeText(repo.defaultBranch)}, setup ${repo.setupStatus})` + (repo.changeRequestUrl ? `, change request ${escapeText(repo.changeRequestUrl)}` : ""));
84
+ const lines = state.repositories.map((repo, index) => `${index + 1}. ${escapeText(repo.slug)} at ${escapeText(repo.dir)} on ${escapeText(repo.branch ?? "(detached)")} (default ${escapeText(repo.defaultBranch)}, setup ${repo.setupStatus})` + (repo.changeRequestUrl ? `, change request ${escapeText(repo.changeRequestUrl)}` : ""));
85
85
  const only = state.repositories.length === 1 ? state.repositories[0] : void 0;
86
86
  const contents = (only ? `Factory environment: 1 repository, ${escapeText(only.slug)} at ${escapeText(only.dir)}, your working directory.\n` : `Factory environment: ${state.repositories.length} repositories under ${escapeText(state.workingDirectory)}, your working directory.\n`) + lines.join("\n") + (only ? "" : "\nRun git and project commands inside the repository directory they belong to.") + (state.note ? `\n${escapeText(state.note)}` : "");
87
87
  return {
@@ -99,7 +99,6 @@ var FactoryEnvironmentStateProcessor = class {
99
99
  };
100
100
  }
101
101
  };
102
- //#endregion
103
102
  export { FactoryEnvironmentStateProcessor, __clearSessionEnvironmentsForTests, clearSessionEnvironment, peekSessionEnvironment, peekSessionEnvironmentTeardown, recordSessionEnvironment, setSessionEnvironmentNote, updateSessionEnvironmentRepository };
104
103
 
105
104
  //# sourceMappingURL=environment-state-processor.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"environment-state-processor.js","names":[],"sources":["../../src/session/environment-state-processor.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport type { ComputeStateSignalArgs, ComputeStateSignalResult, Processor } from '@mastra/core/processors';\nimport type { RequestContext } from '@mastra/core/request-context';\n\nconst STATE_ID = 'factory-environment';\n\nexport type EnvironmentRepositorySetupStatus = 'ok' | 'failed' | 'skipped';\n\n/** One repository of the booted environment as the agent is told about it: never a token or a clone URL. */\nexport interface SessionEnvironmentRepositoryState {\n slug: string;\n /** Absolute directory of the checkout under the workspace root. */\n dir: string;\n /** The branch the checkout ended on after boot; `null` when it stays detached. */\n branch: string | null;\n defaultBranch: string;\n position: number;\n /** `ok` ran or was already set up, `failed` exited non-zero, `skipped` has no setup command. */\n setupStatus: EnvironmentRepositorySetupStatus;\n /** The change request this session opened in the repository, once it has. */\n changeRequestUrl?: string;\n}\n\nexport interface SessionEnvironmentState {\n /** The workspace root, the session's working directory; repositories sit beneath it. */\n workingDirectory: string;\n repositories: SessionEnvironmentRepositoryState[];\n /** One free-form line the boot wants the agent to know (never a credential), rendered last. */\n note?: string;\n}\n\n// The boot records what it materialized, keyed by the factory session id (the\n// agent's resource id); the processor reads it on the next model step. Boot\n// runs on every sandbox start, so a replacement process re-learns the state\n// the first time the session's sandbox starts again.\nconst environments = new Map<string, { state: SessionEnvironmentState; teardown: SessionEnvironmentTeardown[] }>();\n\n/** A repository's teardown command and the directory it runs in; kept beside the state, never rendered. */\nexport interface SessionEnvironmentTeardown {\n slug: string;\n dir: string;\n command: string;\n}\n\nexport function recordSessionEnvironment(\n sessionId: string,\n state: SessionEnvironmentState,\n teardown: SessionEnvironmentTeardown[] = [],\n): void {\n environments.set(sessionId, { state, teardown });\n}\n\n/**\n * Record what a source-control tool did in one environment repository (the\n * branch it pushed, the change request it opened) so the next signal snapshot\n * tells the agent. A no-op when the session's environment is not recorded in\n * this process or the slug is not part of it.\n */\nexport function updateSessionEnvironmentRepository(\n sessionId: string,\n slug: string,\n patch: { branch?: string; changeRequestUrl?: string },\n): void {\n const entry = environments.get(sessionId);\n if (!entry) return;\n const wanted = slug.toLowerCase();\n const repositories = entry.state.repositories.map(repo => {\n if (repo.slug.toLowerCase() !== wanted) return repo;\n return {\n ...repo,\n ...(patch.branch !== undefined ? { branch: patch.branch } : {}),\n ...(patch.changeRequestUrl !== undefined ? { changeRequestUrl: patch.changeRequestUrl } : {}),\n };\n });\n entry.state = { ...entry.state, repositories };\n}\n\n/** Set or clear the environment's note; a no-op when the session's environment is not recorded here. */\nexport function setSessionEnvironmentNote(sessionId: string, note: string | null): void {\n const entry = environments.get(sessionId);\n if (!entry) return;\n const { note: _previous, ...rest } = entry.state;\n entry.state = note ? { ...rest, note } : rest;\n}\n\nexport function clearSessionEnvironment(sessionId: string): void {\n environments.delete(sessionId);\n}\n\nexport function peekSessionEnvironment(sessionId: string): SessionEnvironmentState | undefined {\n return environments.get(sessionId)?.state;\n}\n\n/** Teardown commands of the booted environment, in position order. */\nexport function peekSessionEnvironmentTeardown(sessionId: string): SessionEnvironmentTeardown[] {\n return environments.get(sessionId)?.teardown ?? [];\n}\n\nexport function __clearSessionEnvironmentsForTests(): void {\n environments.clear();\n}\n\nfunction escapeText(value: string): string {\n return value.replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;');\n}\n\n// Unlike factory runs, a user session carries no `factoryProjectId` in its\n// controller state, and the environment is recorded for both, so only the\n// session id is required.\nfunction sessionIdFromContext(requestContext: RequestContext | undefined): string | null {\n if (!requestContext || typeof requestContext.get !== 'function') return null;\n const controller = requestContext.get('controller') as { sessionId?: unknown } | undefined;\n return typeof controller?.sessionId === 'string' && controller.sessionId ? controller.sessionId : null;\n}\n\nfunction environmentCacheKey(state: SessionEnvironmentState): string {\n return `environment:${createHash('sha256').update(JSON.stringify(state)).digest('hex').slice(0, 16)}`;\n}\n\n/**\n * Tells the agent which repositories its sandbox holds and where: the\n * workspace root is the session's working directory and every environment\n * repository is a directory beneath it. Snapshot mode only; a new snapshot\n * is emitted when the booted set changes (a start after an environment edit).\n */\nexport class FactoryEnvironmentStateProcessor implements Processor<'factory-environment'> {\n readonly id = STATE_ID;\n readonly stateId = STATE_ID;\n\n async computeStateSignal(args: ComputeStateSignalArgs): Promise<ComputeStateSignalResult> {\n const sessionId = sessionIdFromContext(args.requestContext);\n if (!sessionId) return;\n const state = environments.get(sessionId)?.state;\n if (!state) return;\n const cacheKey = environmentCacheKey(state);\n const hasBase = Boolean(args.lastSnapshot) && args.contextWindow.hasSnapshot;\n if (hasBase && (args.tracking?.currentCacheKey ?? args.lastSnapshot?.metadata?.state?.cacheKey) === cacheKey)\n return;\n const lines = state.repositories.map(\n repo =>\n `${repo.position}. ${escapeText(repo.slug)} at ${escapeText(repo.dir)} on ${escapeText(repo.branch ?? '(detached)')} (default ${escapeText(repo.defaultBranch)}, setup ${repo.setupStatus})` +\n (repo.changeRequestUrl ? `, change request ${escapeText(repo.changeRequestUrl)}` : ''),\n );\n const only = state.repositories.length === 1 ? state.repositories[0] : undefined;\n const contents =\n (only\n ? `Factory environment: 1 repository, ${escapeText(only.slug)} at ${escapeText(only.dir)}, your working directory.\\n`\n : `Factory environment: ${state.repositories.length} repositories under ${escapeText(state.workingDirectory)}, your working directory.\\n`) +\n lines.join('\\n') +\n (only ? '' : '\\nRun git and project commands inside the repository directory they belong to.') +\n (state.note ? `\\n${escapeText(state.note)}` : '');\n return {\n id: STATE_ID,\n cacheKey,\n mode: 'snapshot',\n tagName: 'factory-environment',\n contents,\n value: { environment: state },\n attributes: { workingDirectory: only?.dir ?? state.workingDirectory, repositories: state.repositories.length },\n metadata: { value: { environment: state } },\n };\n }\n}\n"],"mappings":";;AAIA,MAAM,WAAW;AA+BjB,MAAM,+BAAe,IAAI,IAAwF;AASjH,SAAgB,yBACd,WACA,OACA,WAAyC,CAAC,GACpC;CACN,aAAa,IAAI,WAAW;EAAE;EAAO;CAAS,CAAC;AACjD;;;;;;;AAQA,SAAgB,mCACd,WACA,MACA,OACM;CACN,MAAM,QAAQ,aAAa,IAAI,SAAS;CACxC,IAAI,CAAC,OAAO;CACZ,MAAM,SAAS,KAAK,YAAY;CAChC,MAAM,eAAe,MAAM,MAAM,aAAa,KAAI,SAAQ;EACxD,IAAI,KAAK,KAAK,YAAY,MAAM,QAAQ,OAAO;EAC/C,OAAO;GACL,GAAG;GACH,GAAI,MAAM,WAAW,KAAA,IAAY,EAAE,QAAQ,MAAM,OAAO,IAAI,CAAC;GAC7D,GAAI,MAAM,qBAAqB,KAAA,IAAY,EAAE,kBAAkB,MAAM,iBAAiB,IAAI,CAAC;EAC7F;CACF,CAAC;CACD,MAAM,QAAQ;EAAE,GAAG,MAAM;EAAO;CAAa;AAC/C;;AAGA,SAAgB,0BAA0B,WAAmB,MAA2B;CACtF,MAAM,QAAQ,aAAa,IAAI,SAAS;CACxC,IAAI,CAAC,OAAO;CACZ,MAAM,EAAE,MAAM,WAAW,GAAG,SAAS,MAAM;CAC3C,MAAM,QAAQ,OAAO;EAAE,GAAG;EAAM;CAAK,IAAI;AAC3C;AAEA,SAAgB,wBAAwB,WAAyB;CAC/D,aAAa,OAAO,SAAS;AAC/B;AAEA,SAAgB,uBAAuB,WAAwD;CAC7F,OAAO,aAAa,IAAI,SAAS,CAAC,EAAE;AACtC;;AAGA,SAAgB,+BAA+B,WAAiD;CAC9F,OAAO,aAAa,IAAI,SAAS,CAAC,EAAE,YAAY,CAAC;AACnD;AAEA,SAAgB,qCAA2C;CACzD,aAAa,MAAM;AACrB;AAEA,SAAS,WAAW,OAAuB;CACzC,OAAO,MAAM,WAAW,KAAK,OAAO,CAAC,CAAC,WAAW,KAAK,MAAM,CAAC,CAAC,WAAW,KAAK,MAAM;AACtF;AAKA,SAAS,qBAAqB,gBAA2D;CACvF,IAAI,CAAC,kBAAkB,OAAO,eAAe,QAAQ,YAAY,OAAO;CACxE,MAAM,aAAa,eAAe,IAAI,YAAY;CAClD,OAAO,OAAO,YAAY,cAAc,YAAY,WAAW,YAAY,WAAW,YAAY;AACpG;AAEA,SAAS,oBAAoB,OAAwC;CACnE,OAAO,eAAe,WAAW,QAAQ,CAAC,CAAC,OAAO,KAAK,UAAU,KAAK,CAAC,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,MAAM,GAAG,EAAE;AACpG;;;;;;;AAQA,IAAa,mCAAb,MAA0F;CACxF,KAAc;CACd,UAAmB;CAEnB,MAAM,mBAAmB,MAAiE;EACxF,MAAM,YAAY,qBAAqB,KAAK,cAAc;EAC1D,IAAI,CAAC,WAAW;EAChB,MAAM,QAAQ,aAAa,IAAI,SAAS,CAAC,EAAE;EAC3C,IAAI,CAAC,OAAO;EACZ,MAAM,WAAW,oBAAoB,KAAK;EAE1C,IADgB,QAAQ,KAAK,YAAY,KAAK,KAAK,cAAc,gBACjD,KAAK,UAAU,mBAAmB,KAAK,cAAc,UAAU,OAAO,cAAc,UAClG;EACF,MAAM,QAAQ,MAAM,aAAa,KAC/B,SACE,GAAG,KAAK,SAAS,IAAI,WAAW,KAAK,IAAI,EAAE,MAAM,WAAW,KAAK,GAAG,EAAE,MAAM,WAAW,KAAK,UAAU,YAAY,EAAE,YAAY,WAAW,KAAK,aAAa,EAAE,UAAU,KAAK,YAAY,MACzL,KAAK,mBAAmB,oBAAoB,WAAW,KAAK,gBAAgB,MAAM,GACvF;EACA,MAAM,OAAO,MAAM,aAAa,WAAW,IAAI,MAAM,aAAa,KAAK,KAAA;EACvE,MAAM,YACH,OACG,sCAAsC,WAAW,KAAK,IAAI,EAAE,MAAM,WAAW,KAAK,GAAG,EAAE,+BACvF,wBAAwB,MAAM,aAAa,OAAO,sBAAsB,WAAW,MAAM,gBAAgB,EAAE,gCAC/G,MAAM,KAAK,IAAI,KACd,OAAO,KAAK,qFACZ,MAAM,OAAO,KAAK,WAAW,MAAM,IAAI,MAAM;EAChD,OAAO;GACL,IAAI;GACJ;GACA,MAAM;GACN,SAAS;GACT;GACA,OAAO,EAAE,aAAa,MAAM;GAC5B,YAAY;IAAE,kBAAkB,MAAM,OAAO,MAAM;IAAkB,cAAc,MAAM,aAAa;GAAO;GAC7G,UAAU,EAAE,OAAO,EAAE,aAAa,MAAM,EAAE;EAC5C;CACF;AACF"}
1
+ {"version":3,"file":"environment-state-processor.js","names":[],"sources":["../../src/session/environment-state-processor.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport type { ComputeStateSignalArgs, ComputeStateSignalResult, Processor } from '@mastra/core/processors';\nimport type { RequestContext } from '@mastra/core/request-context';\n\nconst STATE_ID = 'factory-environment';\n\nexport type EnvironmentRepositorySetupStatus = 'ok' | 'failed' | 'skipped';\n\n/** One repository of the booted environment as the agent is told about it: never a token or a clone URL. */\nexport interface SessionEnvironmentRepositoryState {\n slug: string;\n /** Absolute directory of the checkout under the workspace root. */\n dir: string;\n /** The branch the checkout ended on after boot; `null` when it stays detached. */\n branch: string | null;\n defaultBranch: string;\n /** `ok` ran or was already set up, `failed` exited non-zero, `skipped` has no setup command. */\n setupStatus: EnvironmentRepositorySetupStatus;\n /** The change request this session opened in the repository, once it has. */\n changeRequestUrl?: string;\n}\n\nexport interface SessionEnvironmentState {\n /** The workspace root, the session's working directory; repositories sit beneath it. */\n workingDirectory: string;\n repositories: SessionEnvironmentRepositoryState[];\n /** One free-form line the boot wants the agent to know (never a credential), rendered last. */\n note?: string;\n}\n\n// The boot records what it materialized, keyed by the factory session id (the\n// agent's resource id); the processor reads it on the next model step. Boot\n// runs on every sandbox start, so a replacement process re-learns the state\n// the first time the session's sandbox starts again.\nconst environments = new Map<string, { state: SessionEnvironmentState; teardown: SessionEnvironmentTeardown[] }>();\n\n/** A repository's teardown command and the directory it runs in; kept beside the state, never rendered. */\nexport interface SessionEnvironmentTeardown {\n slug: string;\n dir: string;\n command: string;\n}\n\nexport function recordSessionEnvironment(\n sessionId: string,\n state: SessionEnvironmentState,\n teardown: SessionEnvironmentTeardown[] = [],\n): void {\n environments.set(sessionId, { state, teardown });\n}\n\n/**\n * Record what a source-control tool did in one environment repository (the\n * branch it pushed, the change request it opened) so the next signal snapshot\n * tells the agent. A no-op when the session's environment is not recorded in\n * this process or the slug is not part of it.\n */\nexport function updateSessionEnvironmentRepository(\n sessionId: string,\n slug: string,\n patch: { branch?: string; changeRequestUrl?: string },\n): void {\n const entry = environments.get(sessionId);\n if (!entry) return;\n const wanted = slug.toLowerCase();\n const repositories = entry.state.repositories.map(repo => {\n if (repo.slug.toLowerCase() !== wanted) return repo;\n return {\n ...repo,\n ...(patch.branch !== undefined ? { branch: patch.branch } : {}),\n ...(patch.changeRequestUrl !== undefined ? { changeRequestUrl: patch.changeRequestUrl } : {}),\n };\n });\n entry.state = { ...entry.state, repositories };\n}\n\n/** Set or clear the environment's note; a no-op when the session's environment is not recorded here. */\nexport function setSessionEnvironmentNote(sessionId: string, note: string | null): void {\n const entry = environments.get(sessionId);\n if (!entry) return;\n const { note: _previous, ...rest } = entry.state;\n entry.state = note ? { ...rest, note } : rest;\n}\n\nexport function clearSessionEnvironment(sessionId: string): void {\n environments.delete(sessionId);\n}\n\nexport function peekSessionEnvironment(sessionId: string): SessionEnvironmentState | undefined {\n return environments.get(sessionId)?.state;\n}\n\n/** Teardown commands of the booted environment, in position order. */\nexport function peekSessionEnvironmentTeardown(sessionId: string): SessionEnvironmentTeardown[] {\n return environments.get(sessionId)?.teardown ?? [];\n}\n\nexport function __clearSessionEnvironmentsForTests(): void {\n environments.clear();\n}\n\nfunction escapeText(value: string): string {\n return value.replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;');\n}\n\n// Unlike factory runs, a user session carries no `factoryProjectId` in its\n// controller state, and the environment is recorded for both, so only the\n// session id is required.\nfunction sessionIdFromContext(requestContext: RequestContext | undefined): string | null {\n if (!requestContext || typeof requestContext.get !== 'function') return null;\n const controller = requestContext.get('controller') as { sessionId?: unknown } | undefined;\n return typeof controller?.sessionId === 'string' && controller.sessionId ? controller.sessionId : null;\n}\n\nfunction environmentCacheKey(state: SessionEnvironmentState): string {\n return `environment:${createHash('sha256').update(JSON.stringify(state)).digest('hex').slice(0, 16)}`;\n}\n\n/**\n * Tells the agent which repositories its sandbox holds and where: the\n * workspace root is the session's working directory and every environment\n * repository is a directory beneath it. Snapshot mode only; a new snapshot\n * is emitted when the booted set changes (a start after an environment edit).\n */\nexport class FactoryEnvironmentStateProcessor implements Processor<'factory-environment'> {\n readonly id = STATE_ID;\n readonly stateId = STATE_ID;\n\n async computeStateSignal(args: ComputeStateSignalArgs): Promise<ComputeStateSignalResult> {\n const sessionId = sessionIdFromContext(args.requestContext);\n if (!sessionId) return;\n const state = environments.get(sessionId)?.state;\n if (!state) return;\n const cacheKey = environmentCacheKey(state);\n const hasBase = Boolean(args.lastSnapshot) && args.contextWindow.hasSnapshot;\n if (hasBase && (args.tracking?.currentCacheKey ?? args.lastSnapshot?.metadata?.state?.cacheKey) === cacheKey)\n return;\n const lines = state.repositories.map(\n (repo, index) =>\n `${index + 1}. ${escapeText(repo.slug)} at ${escapeText(repo.dir)} on ${escapeText(repo.branch ?? '(detached)')} (default ${escapeText(repo.defaultBranch)}, setup ${repo.setupStatus})` +\n (repo.changeRequestUrl ? `, change request ${escapeText(repo.changeRequestUrl)}` : ''),\n );\n const only = state.repositories.length === 1 ? state.repositories[0] : undefined;\n const contents =\n (only\n ? `Factory environment: 1 repository, ${escapeText(only.slug)} at ${escapeText(only.dir)}, your working directory.\\n`\n : `Factory environment: ${state.repositories.length} repositories under ${escapeText(state.workingDirectory)}, your working directory.\\n`) +\n lines.join('\\n') +\n (only ? '' : '\\nRun git and project commands inside the repository directory they belong to.') +\n (state.note ? `\\n${escapeText(state.note)}` : '');\n return {\n id: STATE_ID,\n cacheKey,\n mode: 'snapshot',\n tagName: 'factory-environment',\n contents,\n value: { environment: state },\n attributes: { workingDirectory: only?.dir ?? state.workingDirectory, repositories: state.repositories.length },\n metadata: { value: { environment: state } },\n };\n }\n}\n"],"mappings":"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"}
@@ -4,7 +4,7 @@ import { resolveTenantProviderDefaultModel } from "../routes/tenant-credentials.
4
4
  import { seedSessionOrg } from "./org-seed.js";
5
5
  import { effectiveModelScope } from "../storage/domains/projects/base.js";
6
6
  import { SourceControlConnectionNotFoundError } from "../storage/domains/source-control/base.js";
7
- //#region src/session/factory-session.ts
7
+ // src/session/factory-session.ts
8
8
  var FactorySourceControlConflictError = class extends Error {};
9
9
  /** Resolve the model and exclusive credential scope for a Factory session. */
10
10
  async function resolveFactorySessionModel(args) {
@@ -250,7 +250,6 @@ async function hydrateFactorySession(session, args) {
250
250
  }
251
251
  return switched ? selectedModelId : void 0;
252
252
  }
253
- //#endregion
254
253
  export { FactorySourceControlConflictError, FactorySourceSessionResolutionError, createSourceControlSessionLookup, ensureFactorySourceSession, hydrateFactorySession, resolveFactorySessionModel, resolveFactorySourceControl, resolvePrimaryEnvironmentRepository };
255
254
 
256
255
  //# sourceMappingURL=factory-session.js.map
@@ -1,7 +1,7 @@
1
1
  import { peekSessionSandbox, sessionLayout } from "../sandbox/session-sandbox.js";
2
2
  import { peekSessionEnvironment } from "./environment-state-processor.js";
3
3
  import { isMeaningfulToolName } from "./first-exec-capture.js";
4
- //#region src/session/filesystem-capture.ts
4
+ // src/session/filesystem-capture.ts
5
5
  /** Exit code of `GIT_CHANGED_FILES_SCRIPT` when the checkout is not there: the repository is skipped, not failed. */
6
6
  const CHECKOUT_MISSING_EXIT_CODE = 3;
7
7
  const GIT_CHANGED_FILES_SCRIPT = `
@@ -189,7 +189,7 @@ function observeSessionFilesystem(session, dependencies) {
189
189
  suspended.delete(event.toolCallId);
190
190
  if (event.isError || event.denied) return;
191
191
  if (isMeaningfulToolName(toolName)) workspaceTouched = true;
192
- return;
192
+ break;
193
193
  }
194
194
  }
195
195
  });
@@ -224,7 +224,6 @@ function observeSessionFilesystem(session, dependencies) {
224
224
  unsubscribeEnd();
225
225
  };
226
226
  }
227
- //#endregion
228
227
  export { ROOT_FILES_LIST_COMMAND, captureSessionFilesystem, observeSessionFilesystem, parseFilesystemCaptureFiles, touchStamp, waitForPendingFilesystemCapture };
229
228
 
230
229
  //# sourceMappingURL=filesystem-capture.js.map