@mastra/factory 0.12.0-alpha.4 → 0.12.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 (51) hide show
  1. package/CHANGELOG.md +107 -0
  2. package/dist/factory.d.ts.map +1 -1
  3. package/dist/factory.js +7 -0
  4. package/dist/factory.js.map +1 -1
  5. package/dist/integrations/github/commits.d.ts +29 -0
  6. package/dist/integrations/github/commits.d.ts.map +1 -0
  7. package/dist/integrations/github/commits.js +29 -0
  8. package/dist/integrations/github/commits.js.map +1 -0
  9. package/dist/integrations/github/routes.d.ts.map +1 -1
  10. package/dist/integrations/github/routes.js +35 -0
  11. package/dist/integrations/github/routes.js.map +1 -1
  12. package/dist/routes/projects.d.ts.map +1 -1
  13. package/dist/routes/projects.js +4 -0
  14. package/dist/routes/projects.js.map +1 -1
  15. package/dist/routes/skills.d.ts.map +1 -1
  16. package/dist/routes/skills.js +9 -3
  17. package/dist/routes/skills.js.map +1 -1
  18. package/dist/routes/surface.d.ts +0 -7
  19. package/dist/routes/surface.d.ts.map +1 -1
  20. package/dist/routes/surface.js +20 -2
  21. package/dist/routes/surface.js.map +1 -1
  22. package/dist/routes/work-items.d.ts.map +1 -1
  23. package/dist/routes/work-items.js +1 -0
  24. package/dist/routes/work-items.js.map +1 -1
  25. package/dist/rules/dispatch-errors.d.ts.map +1 -1
  26. package/dist/rules/dispatch-errors.js +8 -0
  27. package/dist/rules/dispatch-errors.js.map +1 -1
  28. package/dist/rules/dispatcher.d.ts +5 -0
  29. package/dist/rules/dispatcher.d.ts.map +1 -1
  30. package/dist/rules/dispatcher.js +101 -52
  31. package/dist/rules/dispatcher.js.map +1 -1
  32. package/dist/rules/start-coordinator.d.ts +2 -0
  33. package/dist/rules/start-coordinator.d.ts.map +1 -1
  34. package/dist/rules/start-coordinator.js +6 -1
  35. package/dist/rules/start-coordinator.js.map +1 -1
  36. package/dist/skills/service.d.ts +2 -1
  37. package/dist/skills/service.d.ts.map +1 -1
  38. package/dist/skills/service.js +5 -2
  39. package/dist/skills/service.js.map +1 -1
  40. package/dist/storage/domains/projects/base.d.ts +3 -0
  41. package/dist/storage/domains/projects/base.d.ts.map +1 -1
  42. package/dist/storage/domains/projects/base.js +7 -0
  43. package/dist/storage/domains/projects/base.js.map +1 -1
  44. package/dist/storage/domains/source-control/inmemory.d.ts.map +1 -1
  45. package/dist/storage/domains/source-control/inmemory.js +5 -1
  46. package/dist/storage/domains/source-control/inmemory.js.map +1 -1
  47. package/dist/storage/domains/work-items/base.d.ts +9 -1
  48. package/dist/storage/domains/work-items/base.d.ts.map +1 -1
  49. package/dist/storage/domains/work-items/base.js +18 -1
  50. package/dist/storage/domains/work-items/base.js.map +1 -1
  51. package/package.json +5 -5
package/CHANGELOG.md CHANGED
@@ -1,5 +1,112 @@
1
1
  # @mastra/factory
2
2
 
3
+ ## 0.12.0-alpha.5
4
+
5
+ ### Minor Changes
6
+
7
+ - Runs started by the Factory no longer stall without appearing in Needs attention. ([#22530](https://github.com/mastra-ai/mastra/pull/22530))
8
+
9
+ A run that writes a plan used to suspend inside its thread and wait forever: the card said Building, nothing built, and no error appeared anywhere.
10
+
11
+ **Added: an Auto-approve plans switch on the board**
12
+
13
+ Find it in the board's automation settings, beside Auto-start runs. Off by default, which is what runs already did — except a plan nobody is watching now surfaces in Needs attention instead of hanging. On, the Factory answers the plan itself and the run carries the item through to Done. An agent that keeps re-planning is stopped after three approvals and handed to a person.
14
+
15
+ **Fixed: who a parked plan waits for**
16
+
17
+ With the switch off, where a parked plan goes depends on who started the run. A plan on a rule-started run escalates through the rule's own decision, the record Needs attention is built on. A plan on a run a person started keeps waiting for that person, because that pause is the point. With the switch on, the Factory answers both.
18
+
19
+ **Fixed: two smaller holes on the same path**
20
+
21
+ An agent asking to move its own card no longer parks the run behind an approval prompt nobody is watching; the rules engine still governs every move. And a failure that can never succeed on a retry stops burning attempts before it reaches someone.
22
+
23
+ - A Factory run waiting on any answer now surfaces instead of parking silently. ([#22649](https://github.com/mastra-ai/mastra/pull/22649))
24
+
25
+ **Fixed: questions stalled the same way plans used to**
26
+
27
+ The plan gate covered `submit_plan` only. A run that asked a question through `ask_user` still stalled with the card saying Building. Any tool suspension on an unattended run now lands in Needs attention as "Agent is waiting for an answer".
28
+
29
+ **Unchanged: pauses that belong to a person**
30
+
31
+ Person-started runs are untouched — their pauses wait for the person reading them. Auto-approved plans stay the only pause the Factory answers itself, because a question has no approvable default.
32
+
33
+ - Approving a proposed run now happens in the attention inbox instead of sending someone to the Rules page. ([#22709](https://github.com/mastra-ai/mastra/pull/22709))
34
+
35
+ **A queue of proposals is a handful of decisions repeated, not a list of distinct ones.** A rules engine proposes the same run for every card it matches, so fifty rows reading `invokeSkill · triage` said nothing that "32 triage runs" does not. The queue sits grouped by role in a panel above the timeline, collapsed, one line per shape; expanding a group names the work item each proposal is for, so a row finally says what it would start. The banner that only counted the queue is gone, and the sidebar popover's approval link opens the inbox.
36
+
37
+ **A group can be dismissed whole**, behind a confirm step — the way out of a queue nobody wants. There is deliberately no matching "run all": that would bill dozens of agent runs from one click, with no bulk route to make it atomic.
38
+
39
+ **The queue says how much of itself it is showing.** The count in the header is the true pending total; when more proposals exist than one page holds, the panel says how many of them loaded, and the per-group "oldest" timestamp is dropped rather than reporting the oldest of a partial page as the oldest of the queue.
40
+
41
+ **An attention row says what landed as a badge** — `mention`, `comment`, `failed` — carrying the icon of its kind and dimming once read, instead of a coloured bead beside a sentence fragment, with the card as the row's title and the author ahead of the message. The sidebar popover scrolls through its preview again: its list had been capped by the popover's own height rather than the scroller's, which left the scroller measuring no overflow and the popover clipping the rows it could not show.
42
+
43
+ **The Rules page holds only what nothing else shows** now that failures and approvals both land in attention — the full effect lifecycle, decisions with no work item, and the succeeded/dismissed history — so it leaves the Factory navigation and is reached from an attention row or global search.
44
+
45
+ - Added a hands-off start for work items. ([#22652](https://github.com/mastra-ai/mastra/pull/22652))
46
+
47
+ **How it works**
48
+
49
+ Pick "Investigate hands-off" or "Build hands-off" in a card's menu instead of the plain start — restarts too: a card whose run already happened offers "Re-review hands-off" and a hands-off twin of its lane's run. "Prepare approval" has no twin — that run's outcome is a maintainer decision, which hands-off cannot remove. The run's parked plans are approved on your behalf, even while the project's Auto-approve plans switch stays off.
50
+
51
+ The grant sticks to the item, not the run, so the Factory's own follow-up runs on that card stay hands-off too. Other cards keep waiting for plan review, and a hands-off run that keeps re-planning still stops after three approvals.
52
+
53
+ - Factory Overview now shows what landed in the repository, not only what moved on the board. ([#22709](https://github.com/mastra-ai/mastra/pull/22709))
54
+
55
+ A **Latest commits** section reads the connected repository's default branch newest-first, on the same day-rail the Activity and attention pages use: the branch tip carries a ring, everything behind it a filled bead, and each row gives the subject, its author, the short sha and when it landed, opening the commit on GitHub. A Factory with no repository linked says so rather than sitting on a skeleton.
56
+
57
+ A Factory whose board was busy all day but whose main branch has not moved now says so on the page that is supposed to answer that question, instead of sending someone to GitHub to find out. The section leans on `GET /web/github/projects/:id/commits`, so it costs one rate-limited call per visit rather than a poll.
58
+
59
+ - Rebuilt the Factory Overview at `/factories/:id/overview` around what needed a person and what the Factory shipped, and gave board traffic its own page. ([#22709](https://github.com/mastra-ai/mastra/pull/22709))
60
+
61
+ **The page opens on a stage funnel** for the work created in the selected window (7, 30 or 90 days), each card placed by the furthest stage it ever reached rather than by where it sits today.
62
+
63
+ - The saturated core is what got that far with nobody stepping in, the pale sheath what a person had to close. Moves made by Factory rules count as unattended, so the autonomy figures no longer bill automation to a person.
64
+ - Every loss peels off as its own hatched arm, billed to the column the work actually stopped in, so a drop keeps the thickness it cost instead of turning into whitespace.
65
+ - Each column carries its typical hold. Hovering one says how much of it ran hands-off, what it lost since the column before, and what landed as a merged pull request; hovering an arm says how much stopped there and whether it was called off, still holding the column, or left without a decision.
66
+ - The first rung reads Entered, not Intake: that board column also holds live GitHub and Linear candidates with no work item behind them, which the page cannot count.
67
+ - Pull requests the Factory reviewed are counted beside the funnel — a count and only a count, since whether they went on to merge is the team's decision rather than the Factory's work.
68
+
69
+ **Why reach and not time-to-ship.** On a real board almost nothing that lands in Done has passed through Execute and Review: Done is where cards get closed, not where work lands. A "shipped in this window, this fast, this hands-off" figure reads off that same history and cannot stand behind any of the three. Reach can, from the same records, so that is what the page reports.
70
+
71
+ Under the funnel: what is stalled, what is running now, the latest commits, and a preview of activity and of what needs you.
72
+
73
+ **Board traffic has its own Activity page** at `/factories/:id/activity`, reading as a rail cut by day. It shows everything the Factory did, not only stage moves — the board's own stage history for the moves, and the audit trail for the runs started, commits, pushes and comments a move is not, two sources that never describe the same fact.
74
+
75
+ - Each entry reads as a sentence: who acted, what they did, which card, hung off a coloured bead on one continuous rail.
76
+ - A card walked through several stages by one actor folds into a single chain instead of repeating its title, and neighbours saying the same thing about a different card become one sentence with those cards listed in a panel under it.
77
+ - Days are cut by a ruled heading and each row carries its minute on the right edge, so a screenful reads as prose down the middle rather than as a column of timestamps.
78
+
79
+ **The attention inbox and the rule effects page now read on that same rail** — cut by day, each row hanging off the mark of what it is (the kind of message, or the status of the effect) and carrying its time on the right edge, so the three pages are one surface instead of three list styles. The attention inbox also paginates like they do: older items load as the list is scrolled instead of waiting on a click.
80
+
81
+ **Every page opens at its top.** The data router carried the window scroll across navigations, so leaving a scrolled Overview landed on Activity halfway down it.
82
+
83
+ ### Patch Changes
84
+
85
+ - Factory panels now take their shadow and their chart ramp from the design system instead of redeclaring them, so they stay in step with every other surface when those tokens change. ([#22713](https://github.com/mastra-ai/mastra/pull/22713))
86
+
87
+ - Audit log range picking moves off the chart and onto a ruler below it. ([#22709](https://github.com/mastra-ai/mastra/pull/22709))
88
+
89
+ **The chart is display-only.** Marks outside the selected range fade instead of being framed by a drag rectangle, gridlines follow the day ticks and fade out top and bottom, and dashed guides mark the selected limits.
90
+
91
+ **The ruler under it carries a single translucent lens.** Drag its sides to resize, drag its body to slide the window; the day and time of each edge read above and below it. Selection is continuous down to the minute rather than snapped, so a window of a few minutes is as reachable as one of several days, and the exact range also reads out next to the event count. Arrow keys nudge an edge, Escape returns to the full range.
92
+
93
+ **On narrow screens the lens gives way to 1h/6h/24h/7d chips.** A full-width drag surface left no room for precise handles and blocked vertical scrolling.
94
+
95
+ **The axis stops moving under the marks.** It now spans everything loaded rather than the current category filter, so toggling a category no longer rescales it, and the chart holds a fixed height at any width instead of squashing its lanes on a narrow screen. Each category lane carries a faint dotted rule, so a mark reads as sitting on its lane and a chart with nothing to plot still shows its shape rather than going blank.
96
+
97
+ **An empty log says so** instead of drawing a chart over an invented seven-day window — filtering to a category with no events used to shift the axis onto dates where nothing had happened — and the empty states now say what is missing and how to get back.
98
+
99
+ - Fixed authenticated workspace skill runs so tenant OAuth credentials remain available to agent and memory models. ([#22721](https://github.com/mastra-ai/mastra/pull/22721))
100
+
101
+ - Added `GET /web/github/projects/:id/commits` (optional `branch`, `limit`), which lists recent commits for an installed repository. ([#22709](https://github.com/mastra-ai/mastra/pull/22709))
102
+
103
+ It reads with the same installation token that already clones and pushes, rather than through `getInstallationOctokit`: the Platform build answers that call with a stub carrying pull-request reads only, so anything reaching for `repos.*` would have been undefined at runtime while the cast kept the compiler quiet.
104
+
105
+ - Fixed automated runs abandoning their session when a run is re-prepared (for example after a server restart). The run now lands back in the work item's existing session for that role instead of creating a replacement — so the session keeps its original owner instead of switching to whoever approved the run, and no orphaned sandbox is left behind. ([#22410](https://github.com/mastra-ai/mastra/pull/22410))
106
+
107
+ - Updated dependencies [[`733bb9a`](https://github.com/mastra-ai/mastra/commit/733bb9aa28fa35623be50b340b59cd3dd66002c9)]:
108
+ - @mastra/code-sdk@1.6.0-alpha.3
109
+
3
110
  ## 0.12.0-alpha.4
4
111
 
5
112
  ### Patch Changes
@@ -1 +1 @@
1
- {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../src/factory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAMH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAGlD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAWxD,OAAO,KAAK,EAAE,kBAAkB,EAAgD,MAAM,wBAAwB,CAAC;AAwB/G,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAGrD,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,8BAA8B,CAAC;AAE/E,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAmCtE,8EAA8E;AAC9E,MAAM,MAAM,UAAU,GAAG,WAAW,CAAC,qBAAqB,CAAC,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAE9E,MAAM,WAAW,mBAAmB;IAClC;;;;;;;;;;;;;;OAcG;IACH,IAAI,CAAC,EAAE,mBAAmB,GAAG,IAAI,CAAC;IAClC;;;;;;;OAOG;IACH,OAAO,EAAE,cAAc,CAAC;IACxB;;;;OAIG;IACH,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,0EAA0E;IAC1E,OAAO,CAAC,EAAE,0BAA0B,CAAC;IACrC,mDAAmD;IACnD,UAAU,CAAC,EAAE,6BAA6B,CAAC;IAC3C;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,uBAAuB,CAAC;IAC3C;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,kBAAkB,EAAE,CAAC;IACpC;;;;;OAKG;IACH,KAAK,CAAC,EAAE,YAAY,CAAC;IAErB;;;OAGG;IACH,QAAQ,CAAC,EAAE;QACT,aAAa,CAAC,EAAE,MAAM,CAAC;KACxB,CAAC;CACH;AAED,YAAY,EAAE,0BAA0B,EAAE,MAAM,8BAA8B,CAAC;AAE/E;;;;GAIG;AACH,MAAM,WAAW,6BAA6B;IAC5C,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAyED,qBAAa,aAAa;;gBAOZ,MAAM,EAAE,mBAAmB;IAWvC;;;;OAIG;IACG,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC;IAurBpC;;;;OAIG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAY/B,iFAAiF;IAC3E,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAGhC"}
1
+ {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../src/factory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAMH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAGlD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAWxD,OAAO,KAAK,EAAE,kBAAkB,EAAgD,MAAM,wBAAwB,CAAC;AAwB/G,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAGrD,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,8BAA8B,CAAC;AAE/E,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAmCtE,8EAA8E;AAC9E,MAAM,MAAM,UAAU,GAAG,WAAW,CAAC,qBAAqB,CAAC,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAE9E,MAAM,WAAW,mBAAmB;IAClC;;;;;;;;;;;;;;OAcG;IACH,IAAI,CAAC,EAAE,mBAAmB,GAAG,IAAI,CAAC;IAClC;;;;;;;OAOG;IACH,OAAO,EAAE,cAAc,CAAC;IACxB;;;;OAIG;IACH,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,0EAA0E;IAC1E,OAAO,CAAC,EAAE,0BAA0B,CAAC;IACrC,mDAAmD;IACnD,UAAU,CAAC,EAAE,6BAA6B,CAAC;IAC3C;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,uBAAuB,CAAC;IAC3C;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,kBAAkB,EAAE,CAAC;IACpC;;;;;OAKG;IACH,KAAK,CAAC,EAAE,YAAY,CAAC;IAErB;;;OAGG;IACH,QAAQ,CAAC,EAAE;QACT,aAAa,CAAC,EAAE,MAAM,CAAC;KACxB,CAAC;CACH;AAED,YAAY,EAAE,0BAA0B,EAAE,MAAM,8BAA8B,CAAC;AAE/E;;;;GAIG;AACH,MAAM,WAAW,6BAA6B;IAC5C,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAyED,qBAAa,aAAa;;gBAOZ,MAAM,EAAE,mBAAmB;IAWvC;;;;OAIG;IACG,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC;IA4rBpC;;;;OAIG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAY/B,iFAAiF;IAC3E,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAGhC"}
package/dist/factory.js CHANGED
@@ -423,6 +423,13 @@ var MastraFactory = class {
423
423
  id: factoryProjectId
424
424
  }))?.autoRunEnabled ?? false;
425
425
  },
426
+ autoApprovePlans: async ({ orgId, factoryProjectId }) => {
427
+ await factoryProjectsStorage.ensureReady();
428
+ return (await factoryProjectsStorage.get({
429
+ orgId,
430
+ id: factoryProjectId
431
+ }))?.autoApprovePlans ?? false;
432
+ },
426
433
  reconcileToolResults: () => factoryProcessor?.reconcileAllBoundThreads() ?? Promise.resolve(),
427
434
  prepareBinding,
428
435
  feedReader: new FactoryFeedReader(workItemCommentsStorage),
@@ -1 +1 @@
1
- {"version":3,"file":"factory.js","names":["#config","#preparing","#dispatcher","#prepared","#factoryProcessor"],"sources":["../src/factory.ts"],"sourcesContent":["/**\n * `MastraFactory` — the single entry point to the whole Mastra Software Factory.\n *\n * The consumer's deploy entry constructs deployment-specific config instances\n * (auth adapter, pubsub) and passes them here explicitly. The only provider\n * defaults constructed here are Platform GitHub and Linear integrations when\n * Platform credentials exist and the caller did not provide those integrations.\n *\n * `prepare()` resolves feature readiness, threads every dependency explicitly,\n * assembles the web routes/middleware, and returns the constructor args for\n * `new Mastra(...)`. The literal `export const mastra = new Mastra(...)` must\n * stay in the entry file — the deployer's `checkConfigExport` Babel plugin\n * only marks the config valid when it finds that literal in the entry AST —\n * so the factory produces args instead of the instance. `finalize()` runs the\n * post-construct boot (controller init + workers).\n *\n * Integration readiness is derived from each instance's declared capabilities\n * and the storage domains those capabilities require.\n */\n\nimport { MastraAuthStudio } from '@mastra/auth-studio';\nimport { prepareAgentControllerMount } from '@mastra/code-sdk';\nimport { AgentControllerChannels } from '@mastra/core/channels';\nimport { EventEmitterPubSub } from '@mastra/core/events';\nimport type { PubSub } from '@mastra/core/events';\nimport type { Mastra } from '@mastra/core/mastra';\nimport type { RequestContext } from '@mastra/core/request-context';\nimport { hasAuthInit, isUserProvider } from '@mastra/core/server';\nimport type { IMastraAuthProvider } from '@mastra/core/server';\nimport type { FactoryStorage } from '@mastra/core/storage';\nimport type { MastraVector } from '@mastra/core/vector';\nimport type { WorkspaceSandbox } from '@mastra/core/workspace';\nimport {\n buildAuthRoutes,\n createFactoryAuthGate,\n createFactoryRouteAuth,\n getFactoryAuthOrgId,\n getFactoryAuthUserFromContext,\n getFactoryAuthUserId,\n} from './auth.js';\nimport { touchFeed } from './feed-events.js';\nimport type { FactoryIntegration, IntegrationPostToolContext, IntegrationTools } from './integrations/base.js';\nimport type { GithubIntegration } from './integrations/github/integration.js';\nimport {\n recordFactoryPullRequestProvenance,\n resolveFactoryPullRequestParentWorkItemId,\n} from './integrations/github/provenance.js';\nimport type { FactoryPullRequestProvenanceData } from './integrations/github/provenance.js';\nimport { PlatformGithubIntegration } from './integrations/platform/github/integration.js';\nimport { PlatformLinearIntegration } from './integrations/platform/linear/integration.js';\nimport { createCustomProvidersPrimer, registerCustomProvidersSource } from './routes/custom-provider-source.js';\nimport { ProjectRoutes } from './routes/projects.js';\nimport { assembleFactoryApiRoutes, buildIntegrationContext } from './routes/surface.js';\nimport type { FactoryApiRoutesDeps } from './routes/surface.js';\nimport {\n createTenantCredentialPrimer,\n primeTenantCredentials,\n registerTenantCredentialResolver,\n} from './routes/tenant-credentials.js';\nimport { builtInFactoryRules } from './rules/defaults.js';\nimport { FactoryDecisionDispatcher } from './rules/dispatcher.js';\nimport { FactoryPhaseStateProcessor } from './rules/processor.js';\nimport { createTerminalStageCleanup } from './rules/terminal-cleanup.js';\nimport { createFactoryTransitionTools } from './rules/tools.js';\nimport { FactoryTransitionService } from './rules/transition-service.js';\nimport type { FactoryRules } from './rules/types.js';\nimport { assertFactoryRules } from './rules/validation.js';\nimport { SessionRetirementCoordinator } from './sandbox/session-retirement.js';\nimport type { MastraFactorySandboxConfig } from './sandbox/session-sandbox.js';\nimport { createPlaintextFactorySecretEncryption } from './secret-encryption.js';\nimport type { FactorySecretEncryption } from './secret-encryption.js';\nimport { handleServerError } from './server-error.js';\nimport { observeSessionCheckpoint } from './session/checkpoint-capture.js';\nimport { observeSessionFilesystem } from './session/filesystem-capture.js';\nimport { observeSessionFirstExec } from './session/first-exec-capture.js';\nimport { observeSessionFirstMessage } from './session/first-message-capture.js';\nimport { hydrateSessionMemorySettings } from './session/memory-settings-hydration.js';\nimport { hydrateSessionModelPack } from './session/model-pack-hydration.js';\nimport { observeSessionThreadTitle } from './session/thread-title-mirror.js';\nimport { createSpaStaticMiddleware, resolveUiDistDir } from './spa-static.js';\nimport { createStateSigner } from './state-signing.js';\nimport { observeAgentGitAction } from './storage/domains/audit/agent-audit.js';\nimport { AuditStorage } from './storage/domains/audit/base.js';\nimport { AuditDomain } from './storage/domains/audit/domain.js';\nimport { ChannelIdentityStorage } from './storage/domains/channel-identity/base.js';\nimport { WorkItemCommentsStorage } from './storage/domains/comments/base.js';\nimport { CommentsDomain } from './storage/domains/comments/domain.js';\nimport { FactoryFeedReader } from './storage/domains/comments/feed-context.js';\nimport type { WorkItemFeedPublisher } from './storage/domains/comments/feed-sync.js';\nimport { ModelCredentialsStorage } from './storage/domains/credentials/base.js';\nimport { CustomProvidersStorage } from './storage/domains/custom-providers/base.js';\nimport { FilesystemStorage } from './storage/domains/filesystem/base.js';\nimport { IntakeStorage } from './storage/domains/intake/base.js';\nimport { IntegrationStorage } from './storage/domains/integrations/base.js';\nimport { MemorySettingsStorage } from './storage/domains/memory-settings/base.js';\nimport { ModelPacksStorage } from './storage/domains/model-packs/base.js';\nimport { FactoryProjectsStorage } from './storage/domains/projects/base.js';\nimport { QueueHealthStorage } from './storage/domains/queue-health/base.js';\nimport { SourceControlStorage } from './storage/domains/source-control/base.js';\nimport { WorkItemsStorage } from './storage/domains/work-items/base.js';\nimport { timedPhase } from './timing.js';\nimport { createWorkspaceFactory, FactoryWorkspaceRegistry } from './workspace.js';\n\ntype BuildApiRoutesDeps = Pick<FactoryApiRoutesDeps, 'controller' | 'authStorage'>;\n\n/** Constructor args for the `new Mastra(...)` literal in the deploy entry. */\nexport type MastraArgs = NonNullable<ConstructorParameters<typeof Mastra>[0]>;\n\nexport interface MastraFactoryConfig {\n /**\n * Auth provider instance — `MastraAuthStudio` (`@mastra/auth-studio`),\n * `MastraAuthWorkos` (`@mastra/auth-workos`), `MastraAuthBetterAuth`\n * (`@mastra/auth-better-auth`), or any custom `MastraAuthProvider`. Whatever\n * instance is passed is the active provider; a passed instance is always\n * honored as-is.\n *\n * Omitted → the factory defaults to `MastraAuthStudio`, proxying auth\n * through the shared Mastra platform API. `MastraAuthStudio` resolves its\n * own env (`MASTRA_SHARED_API_URL`, `MASTRA_ORGANIZATION_ID`,\n * `MASTRA_COOKIE_DOMAIN`).\n *\n * Pass `null` to disable auth entirely (open server, local-dev behavior)\n * without falling back to the default.\n */\n auth?: IMastraAuthProvider | null;\n /**\n * REQUIRED. Factory storage backend powering BOTH agent storage (threads,\n * messages, memory, OM — via `getMastraStorage()`) and the app tables\n * (projects/source-control/audit/intake — via the generic ops surface). Pass a\n * `PgFactoryStorage` (`@mastra/pg`) for deployments or a\n * `LibSQLFactoryStorage` (`@mastra/libsql`) for local dev — one backend,\n * one connection, every feature on.\n */\n storage: FactoryStorage;\n /**\n * Vector store instance for recall search — `PgVector` (`@mastra/pg`) on\n * the same database as `storage`. Omitted → the SDK mount's default vector\n * store resolution applies.\n */\n vector?: MastraVector;\n /**\n * Distributed event bus instance (e.g. `new RedisStreamsPubSub({ url })`).\n * When set, streams/workflows/signals ride it across processes and the\n * controller drops file-based thread locks in favor of pubsub-coordinated\n * leases. Omitted → in-process default.\n */\n pubsub?: PubSub;\n /**\n * Browser-facing origin used to build integration OAuth/install callback\n * URLs and to derive the auth redirect URI. On the platform the SPA is\n * hosted separately, so this MUST be the public API origin.\n * Default: `http://localhost:4111` (the local Factory server, which also\n * serves the UI).\n */\n publicUrl?: string;\n /**\n * Allowed cross-origin SPA origins. The SPA may be served from a separate\n * static host, so credentialed requests must be explicitly allowed.\n */\n allowedOrigins?: string[];\n /** Sandbox configuration. Omitted → repository sandboxes are disabled. */\n sandbox?: MastraFactorySandboxConfig;\n /** Background Factory dispatcher configuration. */\n dispatcher?: MastraFactoryDispatcherConfig;\n /**\n * Deployment-stable secret for signing integration OAuth `state` values.\n * Omitted → a per-process random secret, which is fine for single-process\n * local development but rejected for integrations that declare\n * `requiresStableStateSigner`.\n */\n stateSecret?: string;\n /**\n * Encryption boundary for persisted model credentials, custom-provider API\n * keys, integration connections, and integration settings. Strongly\n * recommended whenever auth is enabled — omitting it falls back to explicit\n * plaintext compatibility with a boot-time warning.\n */\n secretEncryption?: FactorySecretEncryption;\n /**\n * Registered capability providers. The factory registers the pieces each\n * `FactoryIntegration` instance provides — HTTP routes, storage domains,\n * agent/session tools, intake, source control, and diagnostics — into the\n * system. When Platform credentials are configured, missing `github` and\n * `linear` integrations default to their Platform-backed implementations.\n */\n integrations?: FactoryIntegration[];\n /**\n * Authoritative Factory board, tool-result, and GitHub-event rules. Construct\n * with `defaultFactoryRules({ version, overrides })` so deployment policy has\n * an explicit version and exact handler leaves replace rather than compose.\n * Omitted → conservative built-in rules for the current deployment.\n */\n rules?: FactoryRules;\n\n /**\n * Platform-specific overrides. `githubAppSlug` identifies Factory's own\n * GitHub App writes so their webhook deliveries do not retrigger triage.\n */\n platform?: {\n githubAppSlug?: string;\n };\n}\n\nexport type { MastraFactorySandboxConfig } from './sandbox/session-sandbox.js';\n\n/**\n * Per-process cap on concurrent background Factory dispatches. Omitted means\n * the dispatcher default; this is a local replica budget, not a global queue\n * limit shared across deployments.\n */\nexport interface MastraFactoryDispatcherConfig {\n maxInFlight?: number;\n}\n\nconst CONTROLLER_ID = 'code';\n\nfunction hasPlatformCredentials(): boolean {\n // MASTRA_PLATFORM_ACCESS_TOKEN is the credential Mastra Platform injects\n // into deployed projects; MASTRA_PLATFORM_SECRET_KEY is the org secret key\n // written by project scaffolding. Either enables the platform integrations.\n return Boolean(process.env.MASTRA_PLATFORM_ACCESS_TOKEN?.trim() || process.env.MASTRA_PLATFORM_SECRET_KEY?.trim());\n}\n\n/**\n * Default auth provider — `MastraAuthStudio`, which proxies identity to the\n * shared Mastra platform API. `MastraAuthStudio` resolves `MASTRA_SHARED_API_URL`,\n * `MASTRA_ORGANIZATION_ID`, and `MASTRA_COOKIE_DOMAIN` from env on its own —\n * this helper only derives a cookie-domain fallback from the factory's\n * `publicUrl`.\n *\n * Cookie-domain resolution (Studio picks the first that wins):\n * 1. explicit `MASTRA_COOKIE_DOMAIN` env, if set;\n * 2. `.mastra.ai` when `sharedApiUrl` is on `.mastra.ai`;\n * 3. this parent-domain fallback derived from `publicUrl` — so a deploy on\n * `https://foo.mastra.cloud` mints cookies with `Domain=.mastra.cloud`\n * without the caller wiring the env var by hand.\n * 4. otherwise host-only (no `Domain=`), which is correct for `localhost`.\n */\nfunction buildDefaultStudioAuth(publicUrl: string): IMastraAuthProvider {\n return new MastraAuthStudio({\n cookieDomain: parentDomainFromPublicUrl(publicUrl),\n });\n}\n\n/**\n * Derive a parent cookie domain from `publicUrl` by stripping the leftmost\n * label — the same shape platform-API's env injection uses (see\n * `platform/servers/api/src/lib/studio-env-vars.ts`: `.${routingDomain.replace(/^[^.]+\\./, '')}`).\n *\n * Rather than a generic `strip-left-label` heuristic — which either emits\n * cookies scoped to a public suffix (`sub.example.co.uk` → `.example.co.uk`\n * requires PSL data to be safe) or misclassifies numeric hostnames like\n * `3scale.example.com` as IPv4 — we only derive a parent domain when the\n * host sits under one of the platform's known registrable domains. Anything\n * else (custom domains, arbitrary tenant hostnames, IPs, `localhost`)\n * falls through to host-only cookies. Callers that need a different scope\n * pass `MASTRA_COOKIE_DOMAIN` explicitly (Studio honors that first).\n */\nconst KNOWN_PLATFORM_COOKIE_PARENTS = ['mastra.cloud', 'mastra.ai'] as const;\n\nfunction isIpLiteral(hostname: string): boolean {\n // IPv6 addresses in URLs are bracketed; `URL.hostname` strips the brackets\n // but the address itself still contains `:`. IPv4 is four dot-separated\n // numeric octets — trust the parser to have already validated shape.\n if (hostname.includes(':')) return true;\n return /^\\d+(?:\\.\\d+){3}$/.test(hostname);\n}\n\nfunction parentDomainFromPublicUrl(publicUrl: string): string | undefined {\n let hostname: string;\n try {\n hostname = new URL(publicUrl).hostname.toLowerCase();\n } catch {\n return undefined;\n }\n if (hostname === 'localhost' || isIpLiteral(hostname)) return undefined;\n for (const parent of KNOWN_PLATFORM_COOKIE_PARENTS) {\n // Exact match → we're already on the parent, host-only is correct.\n // Subdomain match → mint the parent-scoped cookie.\n if (hostname === parent) return undefined;\n if (hostname.endsWith(`.${parent}`)) return `.${parent}`;\n }\n return undefined;\n}\n\nexport class MastraFactory {\n readonly #config: MastraFactoryConfig;\n #prepared: Awaited<ReturnType<typeof prepareAgentControllerMount>> | undefined;\n #dispatcher: FactoryDecisionDispatcher | undefined;\n #factoryProcessor: FactoryPhaseStateProcessor | undefined;\n #preparing = false;\n\n constructor(config: MastraFactoryConfig) {\n if (!config?.storage) {\n throw new Error(\n \"MastraFactory: 'storage' is required. Pass a FactoryStorage backend — e.g. \" +\n \"new PgFactoryStorage({ connectionString }) from '@mastra/pg' for deployments, or \" +\n \"new LibSQLFactoryStorage({ url }) from '@mastra/libsql' for local dev.\",\n );\n }\n this.#config = config;\n }\n\n /**\n * Resolve feature readiness, wire every dependency explicitly, and assemble\n * everything needed to construct the server-owned Mastra. Returns the args\n * for the `new Mastra(...)` literal that must live in the entry file.\n */\n async prepare(): Promise<MastraArgs> {\n // Guard set synchronously (before the first await) so overlapping calls —\n // not just strictly sequential ones — can't double-seed the runtime\n // registry or double-run one-time adapter init.\n if (this.#preparing) throw new Error('MastraFactory.prepare() called twice');\n this.#preparing = true;\n\n const publicOrigin = (this.#config.publicUrl ?? 'http://localhost:4111').replace(/\\/+$/, '');\n const allowedOrigins = (this.#config.allowedOrigins ?? []).map(o => o.replace(/\\/+$/, '')).filter(Boolean);\n const storage = this.#config.storage;\n const vector = this.#config.vector;\n const pubsub = this.#config.pubsub;\n // One bus for feed/attention events: a second fallback instance would leave\n // SSE subscribers and producers on different emitters in single-process runs.\n const eventBus = pubsub ?? new EventEmitterPubSub();\n // Default auth: honor an explicitly-passed provider (including `null` to\n // disable auth) as-is; otherwise fall back to `MastraAuthStudio`\n // (platform-proxied identity). The default derives its cookie domain\n // from `publicUrl` — deploys on `<sub>.mastra.cloud` mint parent-domain\n // cookies without the caller wiring `MASTRA_COOKIE_DOMAIN` explicitly.\n const configuredAuth = this.#config.auth;\n const auth: IMastraAuthProvider | undefined =\n configuredAuth === null ? undefined : (configuredAuth ?? buildDefaultStudioAuth(publicOrigin));\n if (auth && !this.#config.secretEncryption) {\n console.warn(\n \"[factory] auth is enabled but 'secretEncryption' is not configured. Persisted model credentials, \" +\n 'custom-provider API keys, and integration secrets will be stored as plaintext. Provide ' +\n \"'secretEncryption' (e.g. via FACTORY_CREDENTIAL_ENCRYPTION_KEY) to encrypt them at rest.\",\n );\n }\n const secretEncryption = this.#config.secretEncryption ?? createPlaintextFactorySecretEncryption();\n // One RouteAuth seam per boot, closed over the resolved provider. Every\n // factory route module receives this handle — no service locator.\n const routeAuth = createFactoryRouteAuth(auth);\n\n // Explicit integrations win. Platform credentials fill only missing GitHub\n // and Linear slots so callers can override either provider independently.\n const integrations = [...(this.#config.integrations ?? [])];\n if (hasPlatformCredentials()) {\n if (!integrations.some(integration => integration.id === 'github')) {\n integrations.push(new PlatformGithubIntegration({ slug: this.#config.platform?.githubAppSlug }));\n }\n if (!integrations.some(integration => integration.id === 'linear')) {\n integrations.push(new PlatformLinearIntegration());\n }\n }\n\n // Validate ids up front so a copy-paste duplicate fails loud instead of one\n // instance silently shadowing the other.\n const integrationIds = new Set<string>();\n for (const integration of integrations) {\n if (integrationIds.has(integration.id)) {\n throw new Error(`MastraFactory: duplicate integration id '${integration.id}' in 'integrations'.`);\n }\n integrationIds.add(integration.id);\n }\n const rules = this.#config.rules ?? builtInFactoryRules();\n assertFactoryRules(rules);\n\n // FactoryStorage owns every app-table domain and initializes them through\n // the same lifecycle as the backend connection.\n const intakeStorage = storage.registerDomain(new IntakeStorage());\n const auditStorage = storage.registerDomain(new AuditStorage());\n const workItemsStorage = storage.registerDomain(new WorkItemsStorage());\n workItemsStorage.onAttentionChanged(scope => touchFeed(eventBus, scope));\n const modelCredentialsStorage = storage.registerDomain(new ModelCredentialsStorage(secretEncryption));\n const modelPacksStorage = storage.registerDomain(new ModelPacksStorage());\n const memorySettingsStorage = storage.registerDomain(new MemorySettingsStorage());\n const customProvidersStorage = storage.registerDomain(new CustomProvidersStorage(secretEncryption));\n const queueHealthStorage = storage.registerDomain(new QueueHealthStorage());\n // Generic integration storage (connections/subscriptions/settings) — the\n // default persistence surface for integrations without a bespoke domain.\n const integrationStorage = storage.registerDomain(new IntegrationStorage(secretEncryption));\n const factoryProjectsStorage = storage.registerDomain(new FactoryProjectsStorage());\n const filesystemStorage = storage.registerDomain(new FilesystemStorage());\n const sourceControlStorage = storage.registerDomain(new SourceControlStorage());\n // Reverse index from a platform sender (Slack/Discord/...) to a Mastra\n // tenant, so inbound channel events can resolve the sender's model creds.\n const channelIdentityStorage = storage.registerDomain(new ChannelIdentityStorage());\n const workItemCommentsStorage = storage.registerDomain(new WorkItemCommentsStorage());\n // Every app-table domain handle the route builders and integrations need,\n // threaded explicitly (no service locator).\n const domains = {\n intake: intakeStorage,\n modelCredentials: modelCredentialsStorage,\n modelPacks: modelPacksStorage,\n memorySettings: memorySettingsStorage,\n customProviders: customProvidersStorage,\n filesystem: filesystemStorage,\n projects: factoryProjectsStorage,\n queueHealth: queueHealthStorage,\n workItems: workItemsStorage,\n channelIdentity: channelIdentityStorage,\n comments: workItemCommentsStorage,\n };\n const auditDomain = new AuditDomain({\n auth: routeAuth,\n audit: auditStorage,\n projects: factoryProjectsStorage,\n users: auth && isUserProvider(auth) ? auth : undefined,\n sinks: integrations,\n agentTenant: requestContext => {\n const user = getFactoryAuthUserFromContext(requestContext);\n return { orgId: getFactoryAuthOrgId(user), userId: getFactoryAuthUserId(user) };\n },\n });\n // Held by reference: the channel attach below pushes into it, long after this.\n const feedPublishers: WorkItemFeedPublisher[] = [];\n const commentsDomain = new CommentsDomain({\n auth: routeAuth,\n comments: workItemCommentsStorage,\n workItems: workItemsStorage,\n projects: factoryProjectsStorage,\n channelIdentity: channelIdentityStorage,\n audit: auditDomain,\n publishers: feedPublishers,\n pubsub: eventBus,\n });\n\n // The sandbox config is a bare callback constructing a session's sandbox\n // from intent. Shape-only validation: probing it with a synthetic ctx at\n // boot would construct against a fake session, so only the type is\n // checked.\n const sandboxConfig = this.#config.sandbox;\n if (sandboxConfig !== undefined && typeof sandboxConfig !== 'function') {\n // An object here is almost certainly the pre-callback config, which\n // described a fleet the factory managed itself. That fleet is gone:\n // sandboxes are per session and the host constructs them, so say what to\n // write instead rather than only naming the expected type.\n if (typeof sandboxConfig === 'object' && sandboxConfig !== null) {\n throw new Error(\n `MastraFactory: 'sandbox' is now a callback, not an options object. It receives a FactorySandboxContext and returns a MastraSandbox, so the host chooses the provider per session:\\n` +\n ` sandbox: ctx => new E2BSandbox({ id: ctx.sessionId })\\n` +\n `The old options map three ways: 'machine' becomes the provider instance you construct inside the callback (one per session instead of one cloned template); 'workdir' is gone — remote providers clone into the VM's home directory and local providers check out under their own workingDirectory; 'maxSandboxes' is gone with the sandbox fleet — there is one sandbox per session and no pool to cap. Omit 'sandbox' entirely to disable sandboxes.`,\n );\n }\n throw new Error(\n `MastraFactory: 'sandbox' must be a function constructing a MastraSandbox from a FactorySandboxContext.`,\n );\n }\n\n const workspaceRegistry = new FactoryWorkspaceRegistry();\n\n // One shared OAuth state signer per boot. The deploy entry supplies a\n // replica-stable secret when needed; otherwise local development gets a\n // per-process random signer (`stable: false`).\n const stateSigner = createStateSigner(this.#config.stateSecret);\n\n // One-time provider initialization with factory-level context (e.g.\n // better-auth builds its default instance on the backend's auth\n // database, WorkOS derives its redirect URI from the public URL).\n // Failures surface here, at prepare() — a misconfigured provider must\n // not boot.\n if (auth && hasAuthInit(auth)) {\n await timedPhase('prepare.auth.init', () =>\n auth.init({ database: storage.authDatabase?.(), publicUrl: publicOrigin, allowedOrigins }),\n );\n }\n\n // Single init path: backend connection failure is a hard boot error;\n // registered app domains initialize fail-soft inside FactoryStorage.\n await timedPhase('prepare.storage.init', () => storage.init());\n\n // Per-tenant model credentials: once the credentials domain is up, model\n // resolution goes through the caller's own store and the SDK stops\n // mirroring stored API keys into process.env.\n //\n // Only register when a real auth adapter gates callers. In local /\n // auth-disabled mode there is no authenticated tenant, so registering would\n // force every model call through an empty tenant store (fail-closed, no env\n // fallback) and break chat with \"Not logged in\". Leaving it unregistered\n // lets the SDK fall back to the file-backed AuthStorage (auth.json) — the\n // same store the local /login and Settings pages read and write.\n if (auth) {\n registerTenantCredentialResolver(modelCredentialsStorage);\n }\n\n // Custom providers: DB-backed in both modes (org rows in tenant mode, the\n // sentinel `local` org in no-auth mode). Once registered, model resolution\n // and the gateway catalog never read settings.json custom providers.\n registerCustomProvidersSource({ storage: customProvidersStorage, authEnabled: Boolean(auth) });\n\n for (const integration of integrations) {\n integration.initialize?.({\n storage: integrationStorage.forIntegration(integration.id),\n projects: factoryProjectsStorage,\n auth: routeAuth,\n });\n if (integration.versionControl) {\n integration.versionControl.initialize({\n storage: sourceControlStorage.forIntegration(integration.id),\n });\n }\n }\n\n // Every integration uses generic integration storage. Version-control\n // providers additionally require the source-control storage domain. Readiness\n // is derived solely from capability presence, never from provider ids.\n const integrationRegistrations = integrations.map(integration => {\n const requiredDomains = [\n 'integrations',\n ...(integration.versionControl ? ['source-control'] : []),\n // Channels resolve an inbound sender to a tenant through the reverse\n // index; without it every message would dispatch tenant-less.\n ...(integration.channels ? ['channel-identity'] : []),\n ];\n return {\n integration,\n ready: requiredDomains.every(domain => storage.isDomainReady(domain)),\n ensureReady: async () => {\n for (const domain of requiredDomains) await storage.ensureDomainReady(domain);\n },\n };\n });\n const intakeReady =\n integrations.some(integration => integration.intake !== undefined) && storage.isDomainReady('intake');\n const factoryReady = storage.isDomainReady('projects') && storage.isDomainReady('work-items');\n const knowledgeEnabled = process.env.MASTRACODE_EXPERIMENTAL_SUBCONSCIOUS === '1';\n const githubIntegration = integrations.find(integration => integration.id === 'github') as\n | GithubIntegration\n | undefined;\n const workItemsReady = storage.isDomainReady('work-items');\n const sessionRetirement =\n sandboxConfig && storage.isDomainReady('source-control')\n ? new SessionRetirementCoordinator({\n invalidateSession: sessionId => workspaceRegistry.invalidateSession(sessionId),\n })\n : undefined;\n const retireTerminalSessions =\n sessionRetirement && githubIntegration && workItemsReady\n ? async ({ orgId, workItemId }: { orgId: string; workItemId: string }) =>\n sessionRetirement.retireWorkItemSessions({\n workItems: workItemsStorage,\n sourceControl: sourceControlStorage.forIntegration(githubIntegration.id),\n orgId,\n workItemId,\n })\n : undefined;\n // Terminal-stage cleanup: ingest any trailing tool results from the item's\n // bound threads, then revoke the bindings so completed items leave the\n // reconcile walk (the active-binding set otherwise grows forever), and\n // finally release the item's sandboxes. Each step is best-effort — a\n // committed transition never fails on cleanup.\n const onTerminalStage = workItemsReady\n ? createTerminalStageCleanup({\n workItems: workItemsStorage,\n // `factoryProcessor` is assigned below in this scope; the cleanup\n // only runs on transitions long after bootstrap completes.\n reconcileBinding: async (binding): Promise<void> => {\n await factoryProcessor?.reconcileBinding(binding);\n },\n // Session retirement supersedes the older direct sandbox release: it\n // invalidates the session and stops/destroys its sandbox.\n ...(retireTerminalSessions ? { releaseSandboxes: retireTerminalSessions } : {}),\n })\n : retireTerminalSessions;\n const transitionService = workItemsReady\n ? new FactoryTransitionService({\n rules,\n storage: workItemsStorage,\n ...(onTerminalStage ? { onTerminalStage } : {}),\n })\n : undefined;\n const projectRoutes = new ProjectRoutes({\n auth: routeAuth,\n projects: factoryProjectsStorage,\n sourceControl: sourceControlStorage,\n versionControlIntegrationIds: integrations\n .filter(integration => integration.versionControl)\n .map(integration => integration.id),\n ...(sessionRetirement ? { sessionRetirement } : {}),\n ...(workItemsReady ? { workItems: workItemsStorage } : {}),\n });\n const factoryProcessor = workItemsReady\n ? new FactoryPhaseStateProcessor({\n rules,\n storage: workItemsStorage,\n ...(transitionService ? { transitionService } : {}),\n ...(githubIntegration\n ? {\n recordPullRequestProvenance: (input: Parameters<typeof recordFactoryPullRequestProvenance>[4]) =>\n recordFactoryPullRequestProvenance(\n githubIntegration,\n sourceControlStorage.forIntegration('github'),\n integrationStorage.forIntegration('github'),\n workItemsStorage,\n input,\n ),\n }\n : {}),\n messageReader: {\n listMessages: async input => {\n const memory = await storage.getMastraStorage().getStore('memory');\n return memory ? memory.listMessages(input) : { messages: [], hasMore: false };\n },\n },\n })\n : undefined;\n\n // Boot assertion: an active integration that signs OAuth `state` needs a\n // replica-stable signer — a per-process random secret silently breaks the\n // OAuth callback on any replica that didn't sign the state. Fail loud now\n // instead. (The built-ins also assert this inside their readiness gates.)\n for (const { integration } of integrationRegistrations) {\n if (integration.requiresStableStateSigner && !stateSigner.stable) {\n throw new Error(\n `MastraFactory: integration '${integration.id}' signs OAuth state and requires a ` +\n `replica-stable state secret, but none is configured. Set 'stateSecret' on the factory config.`,\n );\n }\n }\n\n // The SDK needs to know which backend the injected Mastra store uses\n // (its own `instanceof` detection breaks when the dependency graph holds\n // duplicate package copies). Resolve it by walking the FactoryStorage\n // prototype chain by class name — the factory can't import the concrete\n // classes since '@mastra/pg' / '@mastra/libsql' are the user's choice.\n const mastraStorageBackend = (() => {\n for (let proto = Object.getPrototypeOf(storage); proto; proto = Object.getPrototypeOf(proto)) {\n if (proto.constructor?.name === 'PgFactoryStorage') return 'pg' as const;\n if (proto.constructor?.name === 'LibSQLFactoryStorage') return 'libsql' as const;\n }\n return undefined;\n })();\n\n // Integrations contributing tools to agent sessions: org-scoped\n // `agentTools` (resolved per request) + session-scoped `sessionTools`.\n const toolIntegrations = integrationRegistrations.filter(\n ({ integration }) => integration.agentTools || integration.sessionTools,\n );\n\n // Build the real production controller (agents, modes, tools, memory, OM,\n // MCP, providers) — identical to the terminal app. Agent state lives in\n // the storage backend's Mastra store alongside the Factory app tables —\n // one shared database for all users, separated by `resourceId` scoping.\n const prepared = await timedPhase('prepare.controllerMount', () =>\n prepareAgentControllerMount({\n controllerId: CONTROLLER_ID,\n workspace: createWorkspaceFactory({\n ...(sandboxConfig ? { sandbox: sandboxConfig } : {}),\n ...(githubIntegration ? { github: githubIntegration } : {}),\n ...(workItemsStorage ? { workItems: workItemsStorage } : {}),\n workspaceRegistry,\n }),\n disableGithubSignals: true,\n // Memory settings live in the factory's `memory-settings` app table (per\n // org/user), so the host machine's TUI settings.json must not seed them.\n disableSettingsOmSeed: true,\n // A factory reads the repository it works on and its skill, never the\n // ~/.claude instructions of whoever hosts the process. On the controller\n // rather than per session, so webhook-recreated sessions keep it too.\n // Blank project identity for the same reason: the SDK's defaults seed\n // sessions with the HOST process's own project root / name / branch,\n // which must never reach a hosted session's prompt. Repo-backed\n // sessions get their real workdir pinned by workspace resolution;\n // chat-only sessions legitimately have no project.\n // Factory sessions also start fail-closed on org classification: the\n // unresolved marker is set at birth, a successful org seed clears it,\n // and a resolved `factoryOrgId` always takes precedence — so a failed\n // (best-effort) seed can never leave a session classified as local.\n initialState: {\n skipGlobalInstructions: true,\n factoryOrgUnresolved: true,\n projectPath: '',\n projectName: '',\n gitBranch: '',\n },\n storage: storage.getMastraStorage(),\n ...(mastraStorageBackend ? { storageBackend: mastraStorageBackend } : {}),\n ...(factoryProcessor ? { inputProcessors: [factoryProcessor] } : {}),\n ...(vector ? { vector } : {}),\n ...(toolIntegrations.length > 0 || (workItemsStorage && transitionService)\n ? {\n extraTools: async ({ requestContext }: { requestContext: RequestContext }) => {\n const tools: IntegrationTools = {};\n const toolOwners = new Map<string, string>();\n const mergeTools = (ownerId: string, contributed: IntegrationTools) => {\n for (const [name, tool] of Object.entries(contributed)) {\n const owner = toolOwners.get(name);\n if (owner) {\n throw new Error(\n `MastraFactory: integration tool '${name}' from '${ownerId}' conflicts with '${owner}'.`,\n );\n }\n toolOwners.set(name, ownerId);\n tools[name] = tool;\n }\n };\n if (workItemsStorage && transitionService) {\n mergeTools(\n 'factory',\n await createFactoryTransitionTools({\n requestContext,\n storage: workItemsStorage,\n transitionService,\n // Heals crash-resumed sessions: recovered addresses re-seed\n // projectRepositoryId/baseRef from the source session record.\n // Only offered while the source-control domain is ready — a\n // throwing lookup would abort recovery's catch block and also\n // skip the metadata baseRef fallback.\n ...(storage.isDomainReady('source-control')\n ? { sessions: sourceControlStorage.forIntegration('github').sessions }\n : {}),\n }),\n );\n }\n for (const { integration, ready, ensureReady } of toolIntegrations) {\n if (!ready && ensureReady) {\n try {\n await ensureReady();\n } catch {\n continue;\n }\n }\n if (integration.agentTools) {\n mergeTools(integration.id, await integration.agentTools({ requestContext }));\n }\n if (integration.sessionTools) {\n mergeTools(integration.id, integration.sessionTools({ requestContext }));\n }\n }\n return tools;\n },\n }\n : {}),\n postToolObserver: async (toolContext: IntegrationPostToolContext) => {\n const requestContext = (toolContext.context as { requestContext?: RequestContext } | undefined)\n ?.requestContext;\n if (requestContext) {\n await observeAgentGitAction({\n audit: auditDomain,\n toolContext: { ...toolContext, context: requestContext },\n });\n }\n await Promise.all(\n integrations.map(async integration => {\n if (!integration.postToolObserver) return;\n try {\n await integration.postToolObserver({ toolContext, requestContext });\n } catch (error) {\n console.warn(`[factory] Integration '${integration.id}' post-tool observer failed:`, error);\n }\n }),\n );\n },\n ...(pubsub ? { pubsub, crossProcessPubSub: true } : {}),\n buildApiRoutes: ({ controller, authStorage }: BuildApiRoutesDeps) => [\n // Public `/auth/*` routes (login/callback/logout/me). Folded in as\n // `apiRoutes` (not plain Hono routes) because the entry can't touch the\n // Hono app the deployer generates. `requiresAuth: false`; the gate\n // skips `/auth/*`.\n ...(auth ? buildAuthRoutes(auth, { publicUrl: publicOrigin }) : []),\n // Custom `/web/*` routes (fs / config / integrations / factory / audit).\n ...assembleFactoryApiRoutes({\n controllerId: CONTROLLER_ID,\n controller,\n auth: routeAuth,\n ...(auth && isUserProvider(auth) ? { users: auth } : {}),\n authStorage,\n audit: auditDomain,\n publicOrigin,\n stateSigner,\n sandbox: sandboxConfig,\n sessionRetirement,\n factoryStorage: storage,\n integrationStorage,\n sourceControlStorage,\n domains,\n feed: commentsDomain,\n integrations: integrationRegistrations,\n intakeReady,\n factoryReady,\n knowledgeEnabled,\n rules,\n factoryTransitionService: transitionService,\n onFactoryRuntime: ({ transitionService: runtimeTransitionService, prepareBinding }) => {\n this.#dispatcher ??= new FactoryDecisionDispatcher({\n controller,\n transitionService: runtimeTransitionService,\n storage: storage.getDomain<WorkItemsStorage>('work-items'),\n maxInFlight: this.#config.dispatcher?.maxInFlight,\n isAutoRunEnabled: async ({ orgId, factoryProjectId }) => {\n await factoryProjectsStorage.ensureReady();\n const project = await factoryProjectsStorage.get({ orgId, id: factoryProjectId });\n return project?.autoRunEnabled ?? false;\n },\n reconcileToolResults: () => factoryProcessor?.reconcileAllBoundThreads() ?? Promise.resolve(),\n prepareBinding,\n feedReader: new FactoryFeedReader(workItemCommentsStorage),\n primeCredentials: tenant => primeTenantCredentials({ tenant, credentials: modelCredentialsStorage }),\n resolveLinkedWorkItemParentId: async ({ orgId, decision }) => {\n if (decision.source !== 'github-pr') return null;\n const repositoryId = decision.metadata?.githubRepositoryId;\n const pullRequestNumber = decision.metadata?.githubPullRequestNumber;\n if (typeof repositoryId !== 'number' || typeof pullRequestNumber !== 'number') return null;\n return resolveFactoryPullRequestParentWorkItemId(\n integrationStorage.forIntegration<\n Record<string, unknown>,\n Record<string, unknown>,\n FactoryPullRequestProvenanceData\n >('github'),\n { orgId, repositoryId, pullRequestNumber },\n );\n },\n });\n },\n }),\n ...projectRoutes.routes(),\n ...auditDomain.routes(),\n ...commentsDomain.routes(),\n ],\n buildServerConfig: () => {\n const cors = allowedOrigins.length ? { cors: { origin: allowedOrigins, credentials: true } } : {};\n // Log route errors with method/path/stack and answer with structured\n // JSON instead of an opaque `Internal Server Error`. Applied by the\n // deployer to both the top-level app and the custom-route sub-app.\n const onError = { onError: handleServerError };\n // Same-origin SPA: when a vite build is present (see resolveUiDistDir),\n // serve it at `/` from this server. Mounted last so the auth gate (when\n // enabled) covers it; it always passes `/api`, `/web`, `/auth` through.\n const uiDist = resolveUiDistDir();\n const spa = uiDist ? [createSpaStaticMiddleware(uiDist)] : [];\n if (!auth) {\n // Auth disabled: no gate. Still prime the sentinel-local custom\n // provider snapshot so model calls see DB rows, then SPA + CORS.\n return {\n middleware: [\n createCustomProvidersPrimer({ auth: routeAuth, storage: customProvidersStorage, authEnabled: false }),\n ...spa,\n ],\n ...cors,\n ...onError,\n };\n }\n\n // Ordered middleware. The deployer applies these AFTER its context\n // middleware sets `c.set('mastra', mastra)` and BEFORE routes, so:\n // 1. gate — validates the auth session, stashes the user, and 401s /\n // redirects unauthenticated requests. Skips public `/auth/*`.\n // 2. primers — hydrate the caller's model-credential and custom\n // provider snapshots so the request's first model call\n // resolves tenant credentials and custom providers.\n // 3. spa — serves the built UI for everything the server doesn't own.\n // `auth` also lands on `server.auth` so the core auth middleware (and\n // Studio's dual-auth routing — see `studio.auth` on the returned args)\n // authenticates core `/api/*` routes with the same provider.\n return {\n auth,\n middleware: [\n createFactoryAuthGate(auth),\n createTenantCredentialPrimer({ auth: routeAuth, credentials: modelCredentialsStorage }),\n createCustomProvidersPrimer({\n auth: routeAuth,\n storage: customProvidersStorage,\n authEnabled: Boolean(auth),\n }),\n ...spa,\n ],\n ...cors,\n ...onError,\n };\n },\n }),\n );\n\n prepared.base.controller.onSessionCreated(session => {\n observeSessionFilesystem(session, {\n filesystem: filesystemStorage,\n sourceControl: sourceControlStorage.forIntegration('github'),\n });\n observeSessionCheckpoint(session);\n observeSessionFirstMessage(session, {\n sourceControl: sourceControlStorage.forIntegration('github'),\n });\n observeSessionFirstExec(session, {\n sourceControl: sourceControlStorage.forIntegration('github'),\n });\n observeSessionThreadTitle(session, {\n sourceControl: sourceControlStorage.forIntegration('github'),\n });\n });\n\n // Blocking: `createSession` awaits this seed, so when hydration succeeds\n // a session's first run starts with the owner's stored OM settings.\n // Best-effort — failures are logged inside the helper, never thrown, and\n // the session then falls back to its persisted/default OM configuration.\n prepared.base.controller.onSessionCreated(\n session =>\n hydrateSessionMemorySettings(session, {\n sourceControl: sourceControlStorage.forIntegration('github'),\n memorySettings: memorySettingsStorage,\n }),\n { blocking: true },\n );\n\n // Personal model packs seed interactive user sessions only. Active Factory\n // run bindings are excluded and continue to use the project default model.\n prepared.base.controller.onSessionCreated(\n session =>\n hydrateSessionModelPack(session, {\n sourceControl: sourceControlStorage.forIntegration('github'),\n workItems: workItemsStorage,\n modelPacks: modelPacksStorage,\n }),\n { blocking: true },\n );\n\n this.#prepared = prepared;\n this.#factoryProcessor = factoryProcessor;\n\n // Chat-platform channels (Slack, Discord, …) contributed by integrations,\n // attached to the mounted controller so inbound platform messages reach\n // the same agents the web UI drives. READY integrations only — readiness\n // means the `channel-identity` domain's `init()` succeeded, so its link\n // table is queryable. Without it a sender can't be resolved to a tenant,\n // and attaching anyway would dispatch runs on default credentials.\n const channelRegistrations = integrationRegistrations.filter(\n ({ integration, ready }) => ready && integration.channels,\n );\n // `setChannels` replaces rather than merges, so a second provider would\n // silently never receive a message. Fail loud instead.\n if (channelRegistrations.length > 1) {\n throw new Error(\n `MastraFactory: integrations [${channelRegistrations\n .map(({ integration }) => integration.id)\n .join(', ')}] all provide channels, but only one may. Remove all but one.`,\n );\n }\n for (const { integration } of channelRegistrations) {\n const context = buildIntegrationContext(\n {\n controller: prepared.base.controller,\n publicOrigin,\n auth: routeAuth,\n stateSigner,\n sandbox: sandboxConfig,\n factoryStorage: storage,\n integrationStorage,\n sourceControlStorage,\n rules,\n factoryReady,\n domains,\n feed: commentsDomain,\n ...(githubIntegration ? { sourceControlOwnerId: 'github' } : {}),\n },\n integration.id,\n );\n // Integrations return a channels CONFIG; the factory owns construction.\n prepared.base.controller.setChannels(new AgentControllerChannels(integration.channels!(context)));\n // A publisher posts through the channel SDK this loop just wired up.\n const publisher = integration.feedPublisher?.(context);\n if (publisher) feedPublishers.push(publisher);\n }\n\n // Integration lifecycle workers (e.g. polling an upstream without\n // webhooks): collected from READY integrations only, folded into the\n // constructor args so `new Mastra(...)` merges them with the default\n // workers and `finalize()`'s `startWorkers()` starts them alongside the\n // built-ins. Never passed for the disabled/not-ready case — a worker for\n // an unavailable integration must not run.\n const integrationWorkers = integrationRegistrations\n .filter(({ integration, ready }) => ready && integration.workers)\n .flatMap(({ integration }) =>\n integration.workers!(\n buildIntegrationContext(\n {\n controller: prepared.base.controller,\n publicOrigin,\n auth: routeAuth,\n stateSigner,\n sandbox: sandboxConfig,\n factoryStorage: storage,\n integrationStorage,\n sourceControlStorage,\n rules,\n factoryReady,\n domains,\n feed: commentsDomain,\n ...(githubIntegration ? { sourceControlOwnerId: 'github' } : {}),\n },\n integration.id,\n ),\n ),\n );\n\n return {\n ...prepared.mastraArgs,\n // Same provider on `studio.auth` as on `server.auth` (buildServerConfig):\n // deployed factories must authenticate BOTH plain API callers and Studio\n // requests (`x-mastra-client-type: studio` routes to `studio.auth`).\n ...(auth ? { studio: { auth } } : {}),\n ...(integrationWorkers.length > 0 ? { workers: integrationWorkers } : {}),\n };\n }\n\n /**\n * Post-construct boot: initialize the controller (which inherits the\n * constructed Mastra's storage) and start its workers. Call AFTER the entry\n * has run `new Mastra(prepare()'s args)`.\n */\n async finalize(): Promise<void> {\n if (!this.#prepared) {\n throw new Error('MastraFactory.finalize() called before prepare()');\n }\n await timedPhase('finalize.controller', () => this.#prepared!.finalize());\n await timedPhase(\n 'finalize.reconcileBoundThreads',\n () => this.#factoryProcessor?.reconcileAllBoundThreads() ?? Promise.resolve(),\n );\n this.#dispatcher?.start();\n }\n\n /** Stop Factory-owned background dispatch before the host process shuts down. */\n async shutdown(): Promise<void> {\n await this.#dispatcher?.stop();\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"factory.js","names":["#config","#preparing","#dispatcher","#prepared","#factoryProcessor"],"sources":["../src/factory.ts"],"sourcesContent":["/**\n * `MastraFactory` — the single entry point to the whole Mastra Software Factory.\n *\n * The consumer's deploy entry constructs deployment-specific config instances\n * (auth adapter, pubsub) and passes them here explicitly. The only provider\n * defaults constructed here are Platform GitHub and Linear integrations when\n * Platform credentials exist and the caller did not provide those integrations.\n *\n * `prepare()` resolves feature readiness, threads every dependency explicitly,\n * assembles the web routes/middleware, and returns the constructor args for\n * `new Mastra(...)`. The literal `export const mastra = new Mastra(...)` must\n * stay in the entry file — the deployer's `checkConfigExport` Babel plugin\n * only marks the config valid when it finds that literal in the entry AST —\n * so the factory produces args instead of the instance. `finalize()` runs the\n * post-construct boot (controller init + workers).\n *\n * Integration readiness is derived from each instance's declared capabilities\n * and the storage domains those capabilities require.\n */\n\nimport { MastraAuthStudio } from '@mastra/auth-studio';\nimport { prepareAgentControllerMount } from '@mastra/code-sdk';\nimport { AgentControllerChannels } from '@mastra/core/channels';\nimport { EventEmitterPubSub } from '@mastra/core/events';\nimport type { PubSub } from '@mastra/core/events';\nimport type { Mastra } from '@mastra/core/mastra';\nimport type { RequestContext } from '@mastra/core/request-context';\nimport { hasAuthInit, isUserProvider } from '@mastra/core/server';\nimport type { IMastraAuthProvider } from '@mastra/core/server';\nimport type { FactoryStorage } from '@mastra/core/storage';\nimport type { MastraVector } from '@mastra/core/vector';\nimport type { WorkspaceSandbox } from '@mastra/core/workspace';\nimport {\n buildAuthRoutes,\n createFactoryAuthGate,\n createFactoryRouteAuth,\n getFactoryAuthOrgId,\n getFactoryAuthUserFromContext,\n getFactoryAuthUserId,\n} from './auth.js';\nimport { touchFeed } from './feed-events.js';\nimport type { FactoryIntegration, IntegrationPostToolContext, IntegrationTools } from './integrations/base.js';\nimport type { GithubIntegration } from './integrations/github/integration.js';\nimport {\n recordFactoryPullRequestProvenance,\n resolveFactoryPullRequestParentWorkItemId,\n} from './integrations/github/provenance.js';\nimport type { FactoryPullRequestProvenanceData } from './integrations/github/provenance.js';\nimport { PlatformGithubIntegration } from './integrations/platform/github/integration.js';\nimport { PlatformLinearIntegration } from './integrations/platform/linear/integration.js';\nimport { createCustomProvidersPrimer, registerCustomProvidersSource } from './routes/custom-provider-source.js';\nimport { ProjectRoutes } from './routes/projects.js';\nimport { assembleFactoryApiRoutes, buildIntegrationContext } from './routes/surface.js';\nimport type { FactoryApiRoutesDeps } from './routes/surface.js';\nimport {\n createTenantCredentialPrimer,\n primeTenantCredentials,\n registerTenantCredentialResolver,\n} from './routes/tenant-credentials.js';\nimport { builtInFactoryRules } from './rules/defaults.js';\nimport { FactoryDecisionDispatcher } from './rules/dispatcher.js';\nimport { FactoryPhaseStateProcessor } from './rules/processor.js';\nimport { createTerminalStageCleanup } from './rules/terminal-cleanup.js';\nimport { createFactoryTransitionTools } from './rules/tools.js';\nimport { FactoryTransitionService } from './rules/transition-service.js';\nimport type { FactoryRules } from './rules/types.js';\nimport { assertFactoryRules } from './rules/validation.js';\nimport { SessionRetirementCoordinator } from './sandbox/session-retirement.js';\nimport type { MastraFactorySandboxConfig } from './sandbox/session-sandbox.js';\nimport { createPlaintextFactorySecretEncryption } from './secret-encryption.js';\nimport type { FactorySecretEncryption } from './secret-encryption.js';\nimport { handleServerError } from './server-error.js';\nimport { observeSessionCheckpoint } from './session/checkpoint-capture.js';\nimport { observeSessionFilesystem } from './session/filesystem-capture.js';\nimport { observeSessionFirstExec } from './session/first-exec-capture.js';\nimport { observeSessionFirstMessage } from './session/first-message-capture.js';\nimport { hydrateSessionMemorySettings } from './session/memory-settings-hydration.js';\nimport { hydrateSessionModelPack } from './session/model-pack-hydration.js';\nimport { observeSessionThreadTitle } from './session/thread-title-mirror.js';\nimport { createSpaStaticMiddleware, resolveUiDistDir } from './spa-static.js';\nimport { createStateSigner } from './state-signing.js';\nimport { observeAgentGitAction } from './storage/domains/audit/agent-audit.js';\nimport { AuditStorage } from './storage/domains/audit/base.js';\nimport { AuditDomain } from './storage/domains/audit/domain.js';\nimport { ChannelIdentityStorage } from './storage/domains/channel-identity/base.js';\nimport { WorkItemCommentsStorage } from './storage/domains/comments/base.js';\nimport { CommentsDomain } from './storage/domains/comments/domain.js';\nimport { FactoryFeedReader } from './storage/domains/comments/feed-context.js';\nimport type { WorkItemFeedPublisher } from './storage/domains/comments/feed-sync.js';\nimport { ModelCredentialsStorage } from './storage/domains/credentials/base.js';\nimport { CustomProvidersStorage } from './storage/domains/custom-providers/base.js';\nimport { FilesystemStorage } from './storage/domains/filesystem/base.js';\nimport { IntakeStorage } from './storage/domains/intake/base.js';\nimport { IntegrationStorage } from './storage/domains/integrations/base.js';\nimport { MemorySettingsStorage } from './storage/domains/memory-settings/base.js';\nimport { ModelPacksStorage } from './storage/domains/model-packs/base.js';\nimport { FactoryProjectsStorage } from './storage/domains/projects/base.js';\nimport { QueueHealthStorage } from './storage/domains/queue-health/base.js';\nimport { SourceControlStorage } from './storage/domains/source-control/base.js';\nimport { WorkItemsStorage } from './storage/domains/work-items/base.js';\nimport { timedPhase } from './timing.js';\nimport { createWorkspaceFactory, FactoryWorkspaceRegistry } from './workspace.js';\n\ntype BuildApiRoutesDeps = Pick<FactoryApiRoutesDeps, 'controller' | 'authStorage'>;\n\n/** Constructor args for the `new Mastra(...)` literal in the deploy entry. */\nexport type MastraArgs = NonNullable<ConstructorParameters<typeof Mastra>[0]>;\n\nexport interface MastraFactoryConfig {\n /**\n * Auth provider instance — `MastraAuthStudio` (`@mastra/auth-studio`),\n * `MastraAuthWorkos` (`@mastra/auth-workos`), `MastraAuthBetterAuth`\n * (`@mastra/auth-better-auth`), or any custom `MastraAuthProvider`. Whatever\n * instance is passed is the active provider; a passed instance is always\n * honored as-is.\n *\n * Omitted → the factory defaults to `MastraAuthStudio`, proxying auth\n * through the shared Mastra platform API. `MastraAuthStudio` resolves its\n * own env (`MASTRA_SHARED_API_URL`, `MASTRA_ORGANIZATION_ID`,\n * `MASTRA_COOKIE_DOMAIN`).\n *\n * Pass `null` to disable auth entirely (open server, local-dev behavior)\n * without falling back to the default.\n */\n auth?: IMastraAuthProvider | null;\n /**\n * REQUIRED. Factory storage backend powering BOTH agent storage (threads,\n * messages, memory, OM — via `getMastraStorage()`) and the app tables\n * (projects/source-control/audit/intake — via the generic ops surface). Pass a\n * `PgFactoryStorage` (`@mastra/pg`) for deployments or a\n * `LibSQLFactoryStorage` (`@mastra/libsql`) for local dev — one backend,\n * one connection, every feature on.\n */\n storage: FactoryStorage;\n /**\n * Vector store instance for recall search — `PgVector` (`@mastra/pg`) on\n * the same database as `storage`. Omitted → the SDK mount's default vector\n * store resolution applies.\n */\n vector?: MastraVector;\n /**\n * Distributed event bus instance (e.g. `new RedisStreamsPubSub({ url })`).\n * When set, streams/workflows/signals ride it across processes and the\n * controller drops file-based thread locks in favor of pubsub-coordinated\n * leases. Omitted → in-process default.\n */\n pubsub?: PubSub;\n /**\n * Browser-facing origin used to build integration OAuth/install callback\n * URLs and to derive the auth redirect URI. On the platform the SPA is\n * hosted separately, so this MUST be the public API origin.\n * Default: `http://localhost:4111` (the local Factory server, which also\n * serves the UI).\n */\n publicUrl?: string;\n /**\n * Allowed cross-origin SPA origins. The SPA may be served from a separate\n * static host, so credentialed requests must be explicitly allowed.\n */\n allowedOrigins?: string[];\n /** Sandbox configuration. Omitted → repository sandboxes are disabled. */\n sandbox?: MastraFactorySandboxConfig;\n /** Background Factory dispatcher configuration. */\n dispatcher?: MastraFactoryDispatcherConfig;\n /**\n * Deployment-stable secret for signing integration OAuth `state` values.\n * Omitted → a per-process random secret, which is fine for single-process\n * local development but rejected for integrations that declare\n * `requiresStableStateSigner`.\n */\n stateSecret?: string;\n /**\n * Encryption boundary for persisted model credentials, custom-provider API\n * keys, integration connections, and integration settings. Strongly\n * recommended whenever auth is enabled — omitting it falls back to explicit\n * plaintext compatibility with a boot-time warning.\n */\n secretEncryption?: FactorySecretEncryption;\n /**\n * Registered capability providers. The factory registers the pieces each\n * `FactoryIntegration` instance provides — HTTP routes, storage domains,\n * agent/session tools, intake, source control, and diagnostics — into the\n * system. When Platform credentials are configured, missing `github` and\n * `linear` integrations default to their Platform-backed implementations.\n */\n integrations?: FactoryIntegration[];\n /**\n * Authoritative Factory board, tool-result, and GitHub-event rules. Construct\n * with `defaultFactoryRules({ version, overrides })` so deployment policy has\n * an explicit version and exact handler leaves replace rather than compose.\n * Omitted → conservative built-in rules for the current deployment.\n */\n rules?: FactoryRules;\n\n /**\n * Platform-specific overrides. `githubAppSlug` identifies Factory's own\n * GitHub App writes so their webhook deliveries do not retrigger triage.\n */\n platform?: {\n githubAppSlug?: string;\n };\n}\n\nexport type { MastraFactorySandboxConfig } from './sandbox/session-sandbox.js';\n\n/**\n * Per-process cap on concurrent background Factory dispatches. Omitted means\n * the dispatcher default; this is a local replica budget, not a global queue\n * limit shared across deployments.\n */\nexport interface MastraFactoryDispatcherConfig {\n maxInFlight?: number;\n}\n\nconst CONTROLLER_ID = 'code';\n\nfunction hasPlatformCredentials(): boolean {\n // MASTRA_PLATFORM_ACCESS_TOKEN is the credential Mastra Platform injects\n // into deployed projects; MASTRA_PLATFORM_SECRET_KEY is the org secret key\n // written by project scaffolding. Either enables the platform integrations.\n return Boolean(process.env.MASTRA_PLATFORM_ACCESS_TOKEN?.trim() || process.env.MASTRA_PLATFORM_SECRET_KEY?.trim());\n}\n\n/**\n * Default auth provider — `MastraAuthStudio`, which proxies identity to the\n * shared Mastra platform API. `MastraAuthStudio` resolves `MASTRA_SHARED_API_URL`,\n * `MASTRA_ORGANIZATION_ID`, and `MASTRA_COOKIE_DOMAIN` from env on its own —\n * this helper only derives a cookie-domain fallback from the factory's\n * `publicUrl`.\n *\n * Cookie-domain resolution (Studio picks the first that wins):\n * 1. explicit `MASTRA_COOKIE_DOMAIN` env, if set;\n * 2. `.mastra.ai` when `sharedApiUrl` is on `.mastra.ai`;\n * 3. this parent-domain fallback derived from `publicUrl` — so a deploy on\n * `https://foo.mastra.cloud` mints cookies with `Domain=.mastra.cloud`\n * without the caller wiring the env var by hand.\n * 4. otherwise host-only (no `Domain=`), which is correct for `localhost`.\n */\nfunction buildDefaultStudioAuth(publicUrl: string): IMastraAuthProvider {\n return new MastraAuthStudio({\n cookieDomain: parentDomainFromPublicUrl(publicUrl),\n });\n}\n\n/**\n * Derive a parent cookie domain from `publicUrl` by stripping the leftmost\n * label — the same shape platform-API's env injection uses (see\n * `platform/servers/api/src/lib/studio-env-vars.ts`: `.${routingDomain.replace(/^[^.]+\\./, '')}`).\n *\n * Rather than a generic `strip-left-label` heuristic — which either emits\n * cookies scoped to a public suffix (`sub.example.co.uk` → `.example.co.uk`\n * requires PSL data to be safe) or misclassifies numeric hostnames like\n * `3scale.example.com` as IPv4 — we only derive a parent domain when the\n * host sits under one of the platform's known registrable domains. Anything\n * else (custom domains, arbitrary tenant hostnames, IPs, `localhost`)\n * falls through to host-only cookies. Callers that need a different scope\n * pass `MASTRA_COOKIE_DOMAIN` explicitly (Studio honors that first).\n */\nconst KNOWN_PLATFORM_COOKIE_PARENTS = ['mastra.cloud', 'mastra.ai'] as const;\n\nfunction isIpLiteral(hostname: string): boolean {\n // IPv6 addresses in URLs are bracketed; `URL.hostname` strips the brackets\n // but the address itself still contains `:`. IPv4 is four dot-separated\n // numeric octets — trust the parser to have already validated shape.\n if (hostname.includes(':')) return true;\n return /^\\d+(?:\\.\\d+){3}$/.test(hostname);\n}\n\nfunction parentDomainFromPublicUrl(publicUrl: string): string | undefined {\n let hostname: string;\n try {\n hostname = new URL(publicUrl).hostname.toLowerCase();\n } catch {\n return undefined;\n }\n if (hostname === 'localhost' || isIpLiteral(hostname)) return undefined;\n for (const parent of KNOWN_PLATFORM_COOKIE_PARENTS) {\n // Exact match → we're already on the parent, host-only is correct.\n // Subdomain match → mint the parent-scoped cookie.\n if (hostname === parent) return undefined;\n if (hostname.endsWith(`.${parent}`)) return `.${parent}`;\n }\n return undefined;\n}\n\nexport class MastraFactory {\n readonly #config: MastraFactoryConfig;\n #prepared: Awaited<ReturnType<typeof prepareAgentControllerMount>> | undefined;\n #dispatcher: FactoryDecisionDispatcher | undefined;\n #factoryProcessor: FactoryPhaseStateProcessor | undefined;\n #preparing = false;\n\n constructor(config: MastraFactoryConfig) {\n if (!config?.storage) {\n throw new Error(\n \"MastraFactory: 'storage' is required. Pass a FactoryStorage backend — e.g. \" +\n \"new PgFactoryStorage({ connectionString }) from '@mastra/pg' for deployments, or \" +\n \"new LibSQLFactoryStorage({ url }) from '@mastra/libsql' for local dev.\",\n );\n }\n this.#config = config;\n }\n\n /**\n * Resolve feature readiness, wire every dependency explicitly, and assemble\n * everything needed to construct the server-owned Mastra. Returns the args\n * for the `new Mastra(...)` literal that must live in the entry file.\n */\n async prepare(): Promise<MastraArgs> {\n // Guard set synchronously (before the first await) so overlapping calls —\n // not just strictly sequential ones — can't double-seed the runtime\n // registry or double-run one-time adapter init.\n if (this.#preparing) throw new Error('MastraFactory.prepare() called twice');\n this.#preparing = true;\n\n const publicOrigin = (this.#config.publicUrl ?? 'http://localhost:4111').replace(/\\/+$/, '');\n const allowedOrigins = (this.#config.allowedOrigins ?? []).map(o => o.replace(/\\/+$/, '')).filter(Boolean);\n const storage = this.#config.storage;\n const vector = this.#config.vector;\n const pubsub = this.#config.pubsub;\n // One bus for feed/attention events: a second fallback instance would leave\n // SSE subscribers and producers on different emitters in single-process runs.\n const eventBus = pubsub ?? new EventEmitterPubSub();\n // Default auth: honor an explicitly-passed provider (including `null` to\n // disable auth) as-is; otherwise fall back to `MastraAuthStudio`\n // (platform-proxied identity). The default derives its cookie domain\n // from `publicUrl` — deploys on `<sub>.mastra.cloud` mint parent-domain\n // cookies without the caller wiring `MASTRA_COOKIE_DOMAIN` explicitly.\n const configuredAuth = this.#config.auth;\n const auth: IMastraAuthProvider | undefined =\n configuredAuth === null ? undefined : (configuredAuth ?? buildDefaultStudioAuth(publicOrigin));\n if (auth && !this.#config.secretEncryption) {\n console.warn(\n \"[factory] auth is enabled but 'secretEncryption' is not configured. Persisted model credentials, \" +\n 'custom-provider API keys, and integration secrets will be stored as plaintext. Provide ' +\n \"'secretEncryption' (e.g. via FACTORY_CREDENTIAL_ENCRYPTION_KEY) to encrypt them at rest.\",\n );\n }\n const secretEncryption = this.#config.secretEncryption ?? createPlaintextFactorySecretEncryption();\n // One RouteAuth seam per boot, closed over the resolved provider. Every\n // factory route module receives this handle — no service locator.\n const routeAuth = createFactoryRouteAuth(auth);\n\n // Explicit integrations win. Platform credentials fill only missing GitHub\n // and Linear slots so callers can override either provider independently.\n const integrations = [...(this.#config.integrations ?? [])];\n if (hasPlatformCredentials()) {\n if (!integrations.some(integration => integration.id === 'github')) {\n integrations.push(new PlatformGithubIntegration({ slug: this.#config.platform?.githubAppSlug }));\n }\n if (!integrations.some(integration => integration.id === 'linear')) {\n integrations.push(new PlatformLinearIntegration());\n }\n }\n\n // Validate ids up front so a copy-paste duplicate fails loud instead of one\n // instance silently shadowing the other.\n const integrationIds = new Set<string>();\n for (const integration of integrations) {\n if (integrationIds.has(integration.id)) {\n throw new Error(`MastraFactory: duplicate integration id '${integration.id}' in 'integrations'.`);\n }\n integrationIds.add(integration.id);\n }\n const rules = this.#config.rules ?? builtInFactoryRules();\n assertFactoryRules(rules);\n\n // FactoryStorage owns every app-table domain and initializes them through\n // the same lifecycle as the backend connection.\n const intakeStorage = storage.registerDomain(new IntakeStorage());\n const auditStorage = storage.registerDomain(new AuditStorage());\n const workItemsStorage = storage.registerDomain(new WorkItemsStorage());\n workItemsStorage.onAttentionChanged(scope => touchFeed(eventBus, scope));\n const modelCredentialsStorage = storage.registerDomain(new ModelCredentialsStorage(secretEncryption));\n const modelPacksStorage = storage.registerDomain(new ModelPacksStorage());\n const memorySettingsStorage = storage.registerDomain(new MemorySettingsStorage());\n const customProvidersStorage = storage.registerDomain(new CustomProvidersStorage(secretEncryption));\n const queueHealthStorage = storage.registerDomain(new QueueHealthStorage());\n // Generic integration storage (connections/subscriptions/settings) — the\n // default persistence surface for integrations without a bespoke domain.\n const integrationStorage = storage.registerDomain(new IntegrationStorage(secretEncryption));\n const factoryProjectsStorage = storage.registerDomain(new FactoryProjectsStorage());\n const filesystemStorage = storage.registerDomain(new FilesystemStorage());\n const sourceControlStorage = storage.registerDomain(new SourceControlStorage());\n // Reverse index from a platform sender (Slack/Discord/...) to a Mastra\n // tenant, so inbound channel events can resolve the sender's model creds.\n const channelIdentityStorage = storage.registerDomain(new ChannelIdentityStorage());\n const workItemCommentsStorage = storage.registerDomain(new WorkItemCommentsStorage());\n // Every app-table domain handle the route builders and integrations need,\n // threaded explicitly (no service locator).\n const domains = {\n intake: intakeStorage,\n modelCredentials: modelCredentialsStorage,\n modelPacks: modelPacksStorage,\n memorySettings: memorySettingsStorage,\n customProviders: customProvidersStorage,\n filesystem: filesystemStorage,\n projects: factoryProjectsStorage,\n queueHealth: queueHealthStorage,\n workItems: workItemsStorage,\n channelIdentity: channelIdentityStorage,\n comments: workItemCommentsStorage,\n };\n const auditDomain = new AuditDomain({\n auth: routeAuth,\n audit: auditStorage,\n projects: factoryProjectsStorage,\n users: auth && isUserProvider(auth) ? auth : undefined,\n sinks: integrations,\n agentTenant: requestContext => {\n const user = getFactoryAuthUserFromContext(requestContext);\n return { orgId: getFactoryAuthOrgId(user), userId: getFactoryAuthUserId(user) };\n },\n });\n // Held by reference: the channel attach below pushes into it, long after this.\n const feedPublishers: WorkItemFeedPublisher[] = [];\n const commentsDomain = new CommentsDomain({\n auth: routeAuth,\n comments: workItemCommentsStorage,\n workItems: workItemsStorage,\n projects: factoryProjectsStorage,\n channelIdentity: channelIdentityStorage,\n audit: auditDomain,\n publishers: feedPublishers,\n pubsub: eventBus,\n });\n\n // The sandbox config is a bare callback constructing a session's sandbox\n // from intent. Shape-only validation: probing it with a synthetic ctx at\n // boot would construct against a fake session, so only the type is\n // checked.\n const sandboxConfig = this.#config.sandbox;\n if (sandboxConfig !== undefined && typeof sandboxConfig !== 'function') {\n // An object here is almost certainly the pre-callback config, which\n // described a fleet the factory managed itself. That fleet is gone:\n // sandboxes are per session and the host constructs them, so say what to\n // write instead rather than only naming the expected type.\n if (typeof sandboxConfig === 'object' && sandboxConfig !== null) {\n throw new Error(\n `MastraFactory: 'sandbox' is now a callback, not an options object. It receives a FactorySandboxContext and returns a MastraSandbox, so the host chooses the provider per session:\\n` +\n ` sandbox: ctx => new E2BSandbox({ id: ctx.sessionId })\\n` +\n `The old options map three ways: 'machine' becomes the provider instance you construct inside the callback (one per session instead of one cloned template); 'workdir' is gone — remote providers clone into the VM's home directory and local providers check out under their own workingDirectory; 'maxSandboxes' is gone with the sandbox fleet — there is one sandbox per session and no pool to cap. Omit 'sandbox' entirely to disable sandboxes.`,\n );\n }\n throw new Error(\n `MastraFactory: 'sandbox' must be a function constructing a MastraSandbox from a FactorySandboxContext.`,\n );\n }\n\n const workspaceRegistry = new FactoryWorkspaceRegistry();\n\n // One shared OAuth state signer per boot. The deploy entry supplies a\n // replica-stable secret when needed; otherwise local development gets a\n // per-process random signer (`stable: false`).\n const stateSigner = createStateSigner(this.#config.stateSecret);\n\n // One-time provider initialization with factory-level context (e.g.\n // better-auth builds its default instance on the backend's auth\n // database, WorkOS derives its redirect URI from the public URL).\n // Failures surface here, at prepare() — a misconfigured provider must\n // not boot.\n if (auth && hasAuthInit(auth)) {\n await timedPhase('prepare.auth.init', () =>\n auth.init({ database: storage.authDatabase?.(), publicUrl: publicOrigin, allowedOrigins }),\n );\n }\n\n // Single init path: backend connection failure is a hard boot error;\n // registered app domains initialize fail-soft inside FactoryStorage.\n await timedPhase('prepare.storage.init', () => storage.init());\n\n // Per-tenant model credentials: once the credentials domain is up, model\n // resolution goes through the caller's own store and the SDK stops\n // mirroring stored API keys into process.env.\n //\n // Only register when a real auth adapter gates callers. In local /\n // auth-disabled mode there is no authenticated tenant, so registering would\n // force every model call through an empty tenant store (fail-closed, no env\n // fallback) and break chat with \"Not logged in\". Leaving it unregistered\n // lets the SDK fall back to the file-backed AuthStorage (auth.json) — the\n // same store the local /login and Settings pages read and write.\n if (auth) {\n registerTenantCredentialResolver(modelCredentialsStorage);\n }\n\n // Custom providers: DB-backed in both modes (org rows in tenant mode, the\n // sentinel `local` org in no-auth mode). Once registered, model resolution\n // and the gateway catalog never read settings.json custom providers.\n registerCustomProvidersSource({ storage: customProvidersStorage, authEnabled: Boolean(auth) });\n\n for (const integration of integrations) {\n integration.initialize?.({\n storage: integrationStorage.forIntegration(integration.id),\n projects: factoryProjectsStorage,\n auth: routeAuth,\n });\n if (integration.versionControl) {\n integration.versionControl.initialize({\n storage: sourceControlStorage.forIntegration(integration.id),\n });\n }\n }\n\n // Every integration uses generic integration storage. Version-control\n // providers additionally require the source-control storage domain. Readiness\n // is derived solely from capability presence, never from provider ids.\n const integrationRegistrations = integrations.map(integration => {\n const requiredDomains = [\n 'integrations',\n ...(integration.versionControl ? ['source-control'] : []),\n // Channels resolve an inbound sender to a tenant through the reverse\n // index; without it every message would dispatch tenant-less.\n ...(integration.channels ? ['channel-identity'] : []),\n ];\n return {\n integration,\n ready: requiredDomains.every(domain => storage.isDomainReady(domain)),\n ensureReady: async () => {\n for (const domain of requiredDomains) await storage.ensureDomainReady(domain);\n },\n };\n });\n const intakeReady =\n integrations.some(integration => integration.intake !== undefined) && storage.isDomainReady('intake');\n const factoryReady = storage.isDomainReady('projects') && storage.isDomainReady('work-items');\n const knowledgeEnabled = process.env.MASTRACODE_EXPERIMENTAL_SUBCONSCIOUS === '1';\n const githubIntegration = integrations.find(integration => integration.id === 'github') as\n | GithubIntegration\n | undefined;\n const workItemsReady = storage.isDomainReady('work-items');\n const sessionRetirement =\n sandboxConfig && storage.isDomainReady('source-control')\n ? new SessionRetirementCoordinator({\n invalidateSession: sessionId => workspaceRegistry.invalidateSession(sessionId),\n })\n : undefined;\n const retireTerminalSessions =\n sessionRetirement && githubIntegration && workItemsReady\n ? async ({ orgId, workItemId }: { orgId: string; workItemId: string }) =>\n sessionRetirement.retireWorkItemSessions({\n workItems: workItemsStorage,\n sourceControl: sourceControlStorage.forIntegration(githubIntegration.id),\n orgId,\n workItemId,\n })\n : undefined;\n // Terminal-stage cleanup: ingest any trailing tool results from the item's\n // bound threads, then revoke the bindings so completed items leave the\n // reconcile walk (the active-binding set otherwise grows forever), and\n // finally release the item's sandboxes. Each step is best-effort — a\n // committed transition never fails on cleanup.\n const onTerminalStage = workItemsReady\n ? createTerminalStageCleanup({\n workItems: workItemsStorage,\n // `factoryProcessor` is assigned below in this scope; the cleanup\n // only runs on transitions long after bootstrap completes.\n reconcileBinding: async (binding): Promise<void> => {\n await factoryProcessor?.reconcileBinding(binding);\n },\n // Session retirement supersedes the older direct sandbox release: it\n // invalidates the session and stops/destroys its sandbox.\n ...(retireTerminalSessions ? { releaseSandboxes: retireTerminalSessions } : {}),\n })\n : retireTerminalSessions;\n const transitionService = workItemsReady\n ? new FactoryTransitionService({\n rules,\n storage: workItemsStorage,\n ...(onTerminalStage ? { onTerminalStage } : {}),\n })\n : undefined;\n const projectRoutes = new ProjectRoutes({\n auth: routeAuth,\n projects: factoryProjectsStorage,\n sourceControl: sourceControlStorage,\n versionControlIntegrationIds: integrations\n .filter(integration => integration.versionControl)\n .map(integration => integration.id),\n ...(sessionRetirement ? { sessionRetirement } : {}),\n ...(workItemsReady ? { workItems: workItemsStorage } : {}),\n });\n const factoryProcessor = workItemsReady\n ? new FactoryPhaseStateProcessor({\n rules,\n storage: workItemsStorage,\n ...(transitionService ? { transitionService } : {}),\n ...(githubIntegration\n ? {\n recordPullRequestProvenance: (input: Parameters<typeof recordFactoryPullRequestProvenance>[4]) =>\n recordFactoryPullRequestProvenance(\n githubIntegration,\n sourceControlStorage.forIntegration('github'),\n integrationStorage.forIntegration('github'),\n workItemsStorage,\n input,\n ),\n }\n : {}),\n messageReader: {\n listMessages: async input => {\n const memory = await storage.getMastraStorage().getStore('memory');\n return memory ? memory.listMessages(input) : { messages: [], hasMore: false };\n },\n },\n })\n : undefined;\n\n // Boot assertion: an active integration that signs OAuth `state` needs a\n // replica-stable signer — a per-process random secret silently breaks the\n // OAuth callback on any replica that didn't sign the state. Fail loud now\n // instead. (The built-ins also assert this inside their readiness gates.)\n for (const { integration } of integrationRegistrations) {\n if (integration.requiresStableStateSigner && !stateSigner.stable) {\n throw new Error(\n `MastraFactory: integration '${integration.id}' signs OAuth state and requires a ` +\n `replica-stable state secret, but none is configured. Set 'stateSecret' on the factory config.`,\n );\n }\n }\n\n // The SDK needs to know which backend the injected Mastra store uses\n // (its own `instanceof` detection breaks when the dependency graph holds\n // duplicate package copies). Resolve it by walking the FactoryStorage\n // prototype chain by class name — the factory can't import the concrete\n // classes since '@mastra/pg' / '@mastra/libsql' are the user's choice.\n const mastraStorageBackend = (() => {\n for (let proto = Object.getPrototypeOf(storage); proto; proto = Object.getPrototypeOf(proto)) {\n if (proto.constructor?.name === 'PgFactoryStorage') return 'pg' as const;\n if (proto.constructor?.name === 'LibSQLFactoryStorage') return 'libsql' as const;\n }\n return undefined;\n })();\n\n // Integrations contributing tools to agent sessions: org-scoped\n // `agentTools` (resolved per request) + session-scoped `sessionTools`.\n const toolIntegrations = integrationRegistrations.filter(\n ({ integration }) => integration.agentTools || integration.sessionTools,\n );\n\n // Build the real production controller (agents, modes, tools, memory, OM,\n // MCP, providers) — identical to the terminal app. Agent state lives in\n // the storage backend's Mastra store alongside the Factory app tables —\n // one shared database for all users, separated by `resourceId` scoping.\n const prepared = await timedPhase('prepare.controllerMount', () =>\n prepareAgentControllerMount({\n controllerId: CONTROLLER_ID,\n workspace: createWorkspaceFactory({\n ...(sandboxConfig ? { sandbox: sandboxConfig } : {}),\n ...(githubIntegration ? { github: githubIntegration } : {}),\n ...(workItemsStorage ? { workItems: workItemsStorage } : {}),\n workspaceRegistry,\n }),\n disableGithubSignals: true,\n // Memory settings live in the factory's `memory-settings` app table (per\n // org/user), so the host machine's TUI settings.json must not seed them.\n disableSettingsOmSeed: true,\n // A factory reads the repository it works on and its skill, never the\n // ~/.claude instructions of whoever hosts the process. On the controller\n // rather than per session, so webhook-recreated sessions keep it too.\n // Blank project identity for the same reason: the SDK's defaults seed\n // sessions with the HOST process's own project root / name / branch,\n // which must never reach a hosted session's prompt. Repo-backed\n // sessions get their real workdir pinned by workspace resolution;\n // chat-only sessions legitimately have no project.\n // Factory sessions also start fail-closed on org classification: the\n // unresolved marker is set at birth, a successful org seed clears it,\n // and a resolved `factoryOrgId` always takes precedence — so a failed\n // (best-effort) seed can never leave a session classified as local.\n initialState: {\n skipGlobalInstructions: true,\n factoryOrgUnresolved: true,\n projectPath: '',\n projectName: '',\n gitBranch: '',\n },\n storage: storage.getMastraStorage(),\n ...(mastraStorageBackend ? { storageBackend: mastraStorageBackend } : {}),\n ...(factoryProcessor ? { inputProcessors: [factoryProcessor] } : {}),\n ...(vector ? { vector } : {}),\n ...(toolIntegrations.length > 0 || (workItemsStorage && transitionService)\n ? {\n extraTools: async ({ requestContext }: { requestContext: RequestContext }) => {\n const tools: IntegrationTools = {};\n const toolOwners = new Map<string, string>();\n const mergeTools = (ownerId: string, contributed: IntegrationTools) => {\n for (const [name, tool] of Object.entries(contributed)) {\n const owner = toolOwners.get(name);\n if (owner) {\n throw new Error(\n `MastraFactory: integration tool '${name}' from '${ownerId}' conflicts with '${owner}'.`,\n );\n }\n toolOwners.set(name, ownerId);\n tools[name] = tool;\n }\n };\n if (workItemsStorage && transitionService) {\n mergeTools(\n 'factory',\n await createFactoryTransitionTools({\n requestContext,\n storage: workItemsStorage,\n transitionService,\n // Heals crash-resumed sessions: recovered addresses re-seed\n // projectRepositoryId/baseRef from the source session record.\n // Only offered while the source-control domain is ready — a\n // throwing lookup would abort recovery's catch block and also\n // skip the metadata baseRef fallback.\n ...(storage.isDomainReady('source-control')\n ? { sessions: sourceControlStorage.forIntegration('github').sessions }\n : {}),\n }),\n );\n }\n for (const { integration, ready, ensureReady } of toolIntegrations) {\n if (!ready && ensureReady) {\n try {\n await ensureReady();\n } catch {\n continue;\n }\n }\n if (integration.agentTools) {\n mergeTools(integration.id, await integration.agentTools({ requestContext }));\n }\n if (integration.sessionTools) {\n mergeTools(integration.id, integration.sessionTools({ requestContext }));\n }\n }\n return tools;\n },\n }\n : {}),\n postToolObserver: async (toolContext: IntegrationPostToolContext) => {\n const requestContext = (toolContext.context as { requestContext?: RequestContext } | undefined)\n ?.requestContext;\n if (requestContext) {\n await observeAgentGitAction({\n audit: auditDomain,\n toolContext: { ...toolContext, context: requestContext },\n });\n }\n await Promise.all(\n integrations.map(async integration => {\n if (!integration.postToolObserver) return;\n try {\n await integration.postToolObserver({ toolContext, requestContext });\n } catch (error) {\n console.warn(`[factory] Integration '${integration.id}' post-tool observer failed:`, error);\n }\n }),\n );\n },\n ...(pubsub ? { pubsub, crossProcessPubSub: true } : {}),\n buildApiRoutes: ({ controller, authStorage }: BuildApiRoutesDeps) => [\n // Public `/auth/*` routes (login/callback/logout/me). Folded in as\n // `apiRoutes` (not plain Hono routes) because the entry can't touch the\n // Hono app the deployer generates. `requiresAuth: false`; the gate\n // skips `/auth/*`.\n ...(auth ? buildAuthRoutes(auth, { publicUrl: publicOrigin }) : []),\n // Custom `/web/*` routes (fs / config / integrations / factory / audit).\n ...assembleFactoryApiRoutes({\n controllerId: CONTROLLER_ID,\n controller,\n auth: routeAuth,\n ...(auth && isUserProvider(auth) ? { users: auth } : {}),\n authStorage,\n audit: auditDomain,\n publicOrigin,\n stateSigner,\n sandbox: sandboxConfig,\n sessionRetirement,\n factoryStorage: storage,\n integrationStorage,\n sourceControlStorage,\n domains,\n feed: commentsDomain,\n integrations: integrationRegistrations,\n intakeReady,\n factoryReady,\n knowledgeEnabled,\n rules,\n factoryTransitionService: transitionService,\n onFactoryRuntime: ({ transitionService: runtimeTransitionService, prepareBinding }) => {\n this.#dispatcher ??= new FactoryDecisionDispatcher({\n controller,\n transitionService: runtimeTransitionService,\n storage: storage.getDomain<WorkItemsStorage>('work-items'),\n maxInFlight: this.#config.dispatcher?.maxInFlight,\n isAutoRunEnabled: async ({ orgId, factoryProjectId }) => {\n await factoryProjectsStorage.ensureReady();\n const project = await factoryProjectsStorage.get({ orgId, id: factoryProjectId });\n return project?.autoRunEnabled ?? false;\n },\n autoApprovePlans: async ({ orgId, factoryProjectId }) => {\n await factoryProjectsStorage.ensureReady();\n const project = await factoryProjectsStorage.get({ orgId, id: factoryProjectId });\n return project?.autoApprovePlans ?? false;\n },\n reconcileToolResults: () => factoryProcessor?.reconcileAllBoundThreads() ?? Promise.resolve(),\n prepareBinding,\n feedReader: new FactoryFeedReader(workItemCommentsStorage),\n primeCredentials: tenant => primeTenantCredentials({ tenant, credentials: modelCredentialsStorage }),\n resolveLinkedWorkItemParentId: async ({ orgId, decision }) => {\n if (decision.source !== 'github-pr') return null;\n const repositoryId = decision.metadata?.githubRepositoryId;\n const pullRequestNumber = decision.metadata?.githubPullRequestNumber;\n if (typeof repositoryId !== 'number' || typeof pullRequestNumber !== 'number') return null;\n return resolveFactoryPullRequestParentWorkItemId(\n integrationStorage.forIntegration<\n Record<string, unknown>,\n Record<string, unknown>,\n FactoryPullRequestProvenanceData\n >('github'),\n { orgId, repositoryId, pullRequestNumber },\n );\n },\n });\n },\n }),\n ...projectRoutes.routes(),\n ...auditDomain.routes(),\n ...commentsDomain.routes(),\n ],\n buildServerConfig: () => {\n const cors = allowedOrigins.length ? { cors: { origin: allowedOrigins, credentials: true } } : {};\n // Log route errors with method/path/stack and answer with structured\n // JSON instead of an opaque `Internal Server Error`. Applied by the\n // deployer to both the top-level app and the custom-route sub-app.\n const onError = { onError: handleServerError };\n // Same-origin SPA: when a vite build is present (see resolveUiDistDir),\n // serve it at `/` from this server. Mounted last so the auth gate (when\n // enabled) covers it; it always passes `/api`, `/web`, `/auth` through.\n const uiDist = resolveUiDistDir();\n const spa = uiDist ? [createSpaStaticMiddleware(uiDist)] : [];\n if (!auth) {\n // Auth disabled: no gate. Still prime the sentinel-local custom\n // provider snapshot so model calls see DB rows, then SPA + CORS.\n return {\n middleware: [\n createCustomProvidersPrimer({ auth: routeAuth, storage: customProvidersStorage, authEnabled: false }),\n ...spa,\n ],\n ...cors,\n ...onError,\n };\n }\n\n // Ordered middleware. The deployer applies these AFTER its context\n // middleware sets `c.set('mastra', mastra)` and BEFORE routes, so:\n // 1. gate — validates the auth session, stashes the user, and 401s /\n // redirects unauthenticated requests. Skips public `/auth/*`.\n // 2. primers — hydrate the caller's model-credential and custom\n // provider snapshots so the request's first model call\n // resolves tenant credentials and custom providers.\n // 3. spa — serves the built UI for everything the server doesn't own.\n // `auth` also lands on `server.auth` so the core auth middleware (and\n // Studio's dual-auth routing — see `studio.auth` on the returned args)\n // authenticates core `/api/*` routes with the same provider.\n return {\n auth,\n middleware: [\n createFactoryAuthGate(auth),\n createTenantCredentialPrimer({ auth: routeAuth, credentials: modelCredentialsStorage }),\n createCustomProvidersPrimer({\n auth: routeAuth,\n storage: customProvidersStorage,\n authEnabled: Boolean(auth),\n }),\n ...spa,\n ],\n ...cors,\n ...onError,\n };\n },\n }),\n );\n\n prepared.base.controller.onSessionCreated(session => {\n observeSessionFilesystem(session, {\n filesystem: filesystemStorage,\n sourceControl: sourceControlStorage.forIntegration('github'),\n });\n observeSessionCheckpoint(session);\n observeSessionFirstMessage(session, {\n sourceControl: sourceControlStorage.forIntegration('github'),\n });\n observeSessionFirstExec(session, {\n sourceControl: sourceControlStorage.forIntegration('github'),\n });\n observeSessionThreadTitle(session, {\n sourceControl: sourceControlStorage.forIntegration('github'),\n });\n });\n\n // Blocking: `createSession` awaits this seed, so when hydration succeeds\n // a session's first run starts with the owner's stored OM settings.\n // Best-effort — failures are logged inside the helper, never thrown, and\n // the session then falls back to its persisted/default OM configuration.\n prepared.base.controller.onSessionCreated(\n session =>\n hydrateSessionMemorySettings(session, {\n sourceControl: sourceControlStorage.forIntegration('github'),\n memorySettings: memorySettingsStorage,\n }),\n { blocking: true },\n );\n\n // Personal model packs seed interactive user sessions only. Active Factory\n // run bindings are excluded and continue to use the project default model.\n prepared.base.controller.onSessionCreated(\n session =>\n hydrateSessionModelPack(session, {\n sourceControl: sourceControlStorage.forIntegration('github'),\n workItems: workItemsStorage,\n modelPacks: modelPacksStorage,\n }),\n { blocking: true },\n );\n\n this.#prepared = prepared;\n this.#factoryProcessor = factoryProcessor;\n\n // Chat-platform channels (Slack, Discord, …) contributed by integrations,\n // attached to the mounted controller so inbound platform messages reach\n // the same agents the web UI drives. READY integrations only — readiness\n // means the `channel-identity` domain's `init()` succeeded, so its link\n // table is queryable. Without it a sender can't be resolved to a tenant,\n // and attaching anyway would dispatch runs on default credentials.\n const channelRegistrations = integrationRegistrations.filter(\n ({ integration, ready }) => ready && integration.channels,\n );\n // `setChannels` replaces rather than merges, so a second provider would\n // silently never receive a message. Fail loud instead.\n if (channelRegistrations.length > 1) {\n throw new Error(\n `MastraFactory: integrations [${channelRegistrations\n .map(({ integration }) => integration.id)\n .join(', ')}] all provide channels, but only one may. Remove all but one.`,\n );\n }\n for (const { integration } of channelRegistrations) {\n const context = buildIntegrationContext(\n {\n controller: prepared.base.controller,\n publicOrigin,\n auth: routeAuth,\n stateSigner,\n sandbox: sandboxConfig,\n factoryStorage: storage,\n integrationStorage,\n sourceControlStorage,\n rules,\n factoryReady,\n domains,\n feed: commentsDomain,\n ...(githubIntegration ? { sourceControlOwnerId: 'github' } : {}),\n },\n integration.id,\n );\n // Integrations return a channels CONFIG; the factory owns construction.\n prepared.base.controller.setChannels(new AgentControllerChannels(integration.channels!(context)));\n // A publisher posts through the channel SDK this loop just wired up.\n const publisher = integration.feedPublisher?.(context);\n if (publisher) feedPublishers.push(publisher);\n }\n\n // Integration lifecycle workers (e.g. polling an upstream without\n // webhooks): collected from READY integrations only, folded into the\n // constructor args so `new Mastra(...)` merges them with the default\n // workers and `finalize()`'s `startWorkers()` starts them alongside the\n // built-ins. Never passed for the disabled/not-ready case — a worker for\n // an unavailable integration must not run.\n const integrationWorkers = integrationRegistrations\n .filter(({ integration, ready }) => ready && integration.workers)\n .flatMap(({ integration }) =>\n integration.workers!(\n buildIntegrationContext(\n {\n controller: prepared.base.controller,\n publicOrigin,\n auth: routeAuth,\n stateSigner,\n sandbox: sandboxConfig,\n factoryStorage: storage,\n integrationStorage,\n sourceControlStorage,\n rules,\n factoryReady,\n domains,\n feed: commentsDomain,\n ...(githubIntegration ? { sourceControlOwnerId: 'github' } : {}),\n },\n integration.id,\n ),\n ),\n );\n\n return {\n ...prepared.mastraArgs,\n // Same provider on `studio.auth` as on `server.auth` (buildServerConfig):\n // deployed factories must authenticate BOTH plain API callers and Studio\n // requests (`x-mastra-client-type: studio` routes to `studio.auth`).\n ...(auth ? { studio: { auth } } : {}),\n ...(integrationWorkers.length > 0 ? { workers: integrationWorkers } : {}),\n };\n }\n\n /**\n * Post-construct boot: initialize the controller (which inherits the\n * constructed Mastra's storage) and start its workers. Call AFTER the entry\n * has run `new Mastra(prepare()'s args)`.\n */\n async finalize(): Promise<void> {\n if (!this.#prepared) {\n throw new Error('MastraFactory.finalize() called before prepare()');\n }\n await timedPhase('finalize.controller', () => this.#prepared!.finalize());\n await timedPhase(\n 'finalize.reconcileBoundThreads',\n () => this.#factoryProcessor?.reconcileAllBoundThreads() ?? Promise.resolve(),\n );\n this.#dispatcher?.start();\n }\n\n /** Stop Factory-owned background dispatch before the host process shuts down. */\n async shutdown(): Promise<void> {\n await this.#dispatcher?.stop();\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Read with the installation token that already clones and pushes, not through
3
+ * `getInstallationOctokit`: the Platform deployment answers that with a
4
+ * pull-request-only stub, so `repos.*` is undefined there at runtime while the
5
+ * cast keeps the compiler quiet.
6
+ */
7
+ import type { VersionControl } from '../../capabilities/version-control.js';
8
+ export interface RepositoryCommit {
9
+ sha: string;
10
+ message: string;
11
+ author: string | null;
12
+ avatarUrl: string | null;
13
+ committedAt: string | null;
14
+ url: string;
15
+ }
16
+ export declare function listRepositoryCommits(github: {
17
+ versionControl: Pick<VersionControl, 'getRepositoryAccess'>;
18
+ }, input: {
19
+ orgId: string;
20
+ project: {
21
+ repository: {
22
+ id: string;
23
+ slug: string;
24
+ };
25
+ };
26
+ branch: string;
27
+ limit: number;
28
+ }): Promise<RepositoryCommit[]>;
29
+ //# sourceMappingURL=commits.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commits.d.ts","sourceRoot":"","sources":["../../../src/integrations/github/commits.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAI5E,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,GAAG,EAAE,MAAM,CAAC;CACb;AASD,wBAAsB,qBAAqB,CACzC,MAAM,EAAE;IAAE,cAAc,EAAE,IAAI,CAAC,cAAc,EAAE,qBAAqB,CAAC,CAAA;CAAE,EACvE,KAAK,EAAE;IACL,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE;QAAE,UAAU,EAAE;YAAE,EAAE,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC;IACtD,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf,GACA,OAAO,CAAC,gBAAgB,EAAE,CAAC,CA2B7B"}
@@ -0,0 +1,29 @@
1
+ //#region src/integrations/github/commits.ts
2
+ const GITHUB_API = "https://api.github.com";
3
+ async function listRepositoryCommits(github, input) {
4
+ const access = await github.versionControl.getRepositoryAccess({
5
+ orgId: input.orgId,
6
+ repositoryId: input.project.repository.id
7
+ });
8
+ const query = new URLSearchParams({
9
+ sha: input.branch,
10
+ per_page: String(input.limit)
11
+ });
12
+ const response = await fetch(`${GITHUB_API}/repos/${input.project.repository.slug}/commits?${query}`, { headers: {
13
+ accept: "application/vnd.github+json",
14
+ ...access.authorization ? { authorization: `Bearer ${access.authorization.token}` } : {}
15
+ } });
16
+ if (!response.ok) throw new Error(`GitHub rejected the commit listing for ${input.project.repository.slug} (${response.status}).`);
17
+ return (await response.json()).map((entry) => ({
18
+ sha: entry.sha,
19
+ message: entry.commit.message.split("\n")[0] ?? "",
20
+ author: entry.author?.login ?? entry.commit.author?.name ?? null,
21
+ avatarUrl: entry.author?.avatar_url ?? null,
22
+ committedAt: entry.commit.author?.date ?? null,
23
+ url: entry.html_url
24
+ }));
25
+ }
26
+ //#endregion
27
+ export { listRepositoryCommits };
28
+
29
+ //# sourceMappingURL=commits.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commits.js","names":[],"sources":["../../../src/integrations/github/commits.ts"],"sourcesContent":["/**\n * Read with the installation token that already clones and pushes, not through\n * `getInstallationOctokit`: the Platform deployment answers that with a\n * pull-request-only stub, so `repos.*` is undefined there at runtime while the\n * cast keeps the compiler quiet.\n */\n\nimport type { VersionControl } from '../../capabilities/version-control.js';\n\nconst GITHUB_API = 'https://api.github.com';\n\nexport interface RepositoryCommit {\n sha: string;\n message: string;\n author: string | null;\n avatarUrl: string | null;\n committedAt: string | null;\n url: string;\n}\n\ninterface GithubCommitResponse {\n sha: string;\n html_url: string;\n commit: { message: string; author: { name?: string; date?: string } | null };\n author: { login: string; avatar_url: string } | null;\n}\n\nexport async function listRepositoryCommits(\n github: { versionControl: Pick<VersionControl, 'getRepositoryAccess'> },\n input: {\n orgId: string;\n project: { repository: { id: string; slug: string } };\n branch: string;\n limit: number;\n },\n): Promise<RepositoryCommit[]> {\n const access = await github.versionControl.getRepositoryAccess({\n orgId: input.orgId,\n repositoryId: input.project.repository.id,\n });\n\n const query = new URLSearchParams({ sha: input.branch, per_page: String(input.limit) });\n const response = await fetch(`${GITHUB_API}/repos/${input.project.repository.slug}/commits?${query}`, {\n headers: {\n accept: 'application/vnd.github+json',\n ...(access.authorization ? { authorization: `Bearer ${access.authorization.token}` } : {}),\n },\n });\n\n if (!response.ok) {\n throw new Error(`GitHub rejected the commit listing for ${input.project.repository.slug} (${response.status}).`);\n }\n\n const commits = (await response.json()) as GithubCommitResponse[];\n return commits.map(entry => ({\n sha: entry.sha,\n message: entry.commit.message.split('\\n')[0] ?? '',\n author: entry.author?.login ?? entry.commit.author?.name ?? null,\n avatarUrl: entry.author?.avatar_url ?? null,\n committedAt: entry.commit.author?.date ?? null,\n url: entry.html_url,\n }));\n}\n"],"mappings":";AASA,MAAM,aAAa;AAkBnB,eAAsB,sBACpB,QACA,OAM6B;CAC7B,MAAM,SAAS,MAAM,OAAO,eAAe,oBAAoB;EAC7D,OAAO,MAAM;EACb,cAAc,MAAM,QAAQ,WAAW;CACzC,CAAC;CAED,MAAM,QAAQ,IAAI,gBAAgB;EAAE,KAAK,MAAM;EAAQ,UAAU,OAAO,MAAM,KAAK;CAAE,CAAC;CACtF,MAAM,WAAW,MAAM,MAAM,GAAG,WAAW,SAAS,MAAM,QAAQ,WAAW,KAAK,WAAW,SAAS,EACpG,SAAS;EACP,QAAQ;EACR,GAAI,OAAO,gBAAgB,EAAE,eAAe,UAAU,OAAO,cAAc,QAAQ,IAAI,CAAC;CAC1F,EACF,CAAC;CAED,IAAI,CAAC,SAAS,IACZ,MAAM,IAAI,MAAM,0CAA0C,MAAM,QAAQ,WAAW,KAAK,IAAI,SAAS,OAAO,GAAG;CAIjH,QAAO,MADgB,SAAS,KAAK,EAAA,CACtB,KAAI,WAAU;EAC3B,KAAK,MAAM;EACX,SAAS,MAAM,OAAO,QAAQ,MAAM,IAAI,CAAC,CAAC,MAAM;EAChD,QAAQ,MAAM,QAAQ,SAAS,MAAM,OAAO,QAAQ,QAAQ;EAC5D,WAAW,MAAM,QAAQ,cAAc;EACvC,aAAa,MAAM,OAAO,QAAQ,QAAQ;EAC1C,KAAK,MAAM;CACb,EAAE;AACJ"}
@@ -1 +1 @@
1
- {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../../src/integrations/github/routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAG1D,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGnE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAGvD,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,kCAAkC,CAAC;AAInF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uCAAuC,CAAC;AAC1E,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,+CAA+C,CAAC;AAC3F,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AAQrF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAuC1D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAgBxD,MAAM,WAAW,wBAAwB;IACvC,4EAA4E;IAC5E,IAAI,EAAE,SAAS,CAAC;IAChB,8EAA8E;IAC9E,KAAK,CAAC,EAAE,wBAAwB,CAAC;IACjC;;;;;OAKG;IACH,OAAO,CAAC,EAAE,0BAA0B,CAAC;IACrC,yEAAyE;IACzE,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB;;;;OAIG;IACH,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B;;;;OAIG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iFAAiF;IACjF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4FAA4F;IAC5F,UAAU,CAAC,EAAE,iBAAiB,CAAC,YAAY,CAAC,CAAC;IAC7C,gGAAgG;IAChG,cAAc,EAAE,IAAI,CAAC,qBAAqB,EAAE,KAAK,CAAC,CAAC;IACnD,6EAA6E;IAC7E,SAAS,CAAC,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC;IACjC,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,sBAAsB,CAAC;IAClC,iBAAiB,CAAC,EAAE,OAAO,qCAAqC,EAAE,4BAA4B,CAAC;IAC/F,kFAAkF;IAClF,SAAS,CAAC,EAAE,IAAI,CAAC,OAAO,0CAA0C,EAAE,gBAAgB,EAAE,wBAAwB,CAAC,CAAC;IAChH,+FAA+F;IAC/F,kBAAkB,CAAC,EAAE,CAAC,KAAK,EAAE,mBAAmB,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CACvE;AA4ND;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,QAAQ,EAAE,CAypB/E;AAiHD,KAAK,wBAAwB,GAAG,IAAI,CAAC,aAAa,EAAE,SAAS,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,aAAa,EAAE,UAAU,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../../src/integrations/github/routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAG1D,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGnE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAGvD,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,kCAAkC,CAAC;AAInF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uCAAuC,CAAC;AAC1E,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,+CAA+C,CAAC;AAC3F,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AASrF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAuC1D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAgBxD,MAAM,WAAW,wBAAwB;IACvC,4EAA4E;IAC5E,IAAI,EAAE,SAAS,CAAC;IAChB,8EAA8E;IAC9E,KAAK,CAAC,EAAE,wBAAwB,CAAC;IACjC;;;;;OAKG;IACH,OAAO,CAAC,EAAE,0BAA0B,CAAC;IACrC,yEAAyE;IACzE,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB;;;;OAIG;IACH,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B;;;;OAIG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iFAAiF;IACjF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4FAA4F;IAC5F,UAAU,CAAC,EAAE,iBAAiB,CAAC,YAAY,CAAC,CAAC;IAC7C,gGAAgG;IAChG,cAAc,EAAE,IAAI,CAAC,qBAAqB,EAAE,KAAK,CAAC,CAAC;IACnD,6EAA6E;IAC7E,SAAS,CAAC,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC;IACjC,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,sBAAsB,CAAC;IAClC,iBAAiB,CAAC,EAAE,OAAO,qCAAqC,EAAE,4BAA4B,CAAC;IAC/F,kFAAkF;IAClF,SAAS,CAAC,EAAE,IAAI,CAAC,OAAO,0CAA0C,EAAE,gBAAgB,EAAE,wBAAwB,CAAC,CAAC;IAChH,+FAA+F;IAC/F,kBAAkB,CAAC,EAAE,CAAC,KAAK,EAAE,mBAAmB,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CACvE;AA4ND;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,QAAQ,EAAE,CAypB/E;AAiHD,KAAK,wBAAwB,GAAG,IAAI,CAAC,aAAa,EAAE,SAAS,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,aAAa,EAAE,UAAU,CAAC,CAAC,CAAC"}
@@ -4,6 +4,7 @@ import { requireExec } from "../../sandbox/materialization.js";
4
4
  import { sanitizeSegment } from "../../sandbox/workdir.js";
5
5
  import { peekSessionSandbox } from "../../sandbox/session-sandbox.js";
6
6
  import { normalizeSessionTitle } from "../../session/session-title.js";
7
+ import { listRepositoryCommits } from "./commits.js";
7
8
  import { getGithubFeatureDiagnostics, isGithubFeatureEnabled } from "./config.js";
8
9
  import { reclaimDeletedSessionSandbox } from "./sandbox-release.js";
9
10
  import { MaterializeError, SetupCommandError, commitAll, isValidGitRef as isValidGitRef$1, pushBranch } from "./sandbox.js";
@@ -750,6 +751,7 @@ async function loadOrgProject(options) {
750
751
  if (!project) return { response: c.json({ error: "Project repository not found" }, 404) };
751
752
  return {
752
753
  project,
754
+ orgId,
753
755
  userId
754
756
  };
755
757
  }
@@ -830,6 +832,9 @@ function titleModel(modelId) {
830
832
  requestContext
831
833
  });
832
834
  }
835
+ /** A screenful of history; GitHub caps its own page at 100. */
836
+ const DEFAULT_COMMIT_PAGE = 20;
837
+ const MAX_COMMIT_PAGE = 100;
833
838
  const MAX_SESSION_OWNER_PROFILES = 100;
834
839
  const MAX_SESSION_OWNER_PROFILE_CACHE_ENTRIES = 500;
835
840
  const SESSION_OWNER_PROFILE_TTL_MS = 5 * 6e4;
@@ -1116,6 +1121,36 @@ function buildProjectGitRoutes({ github, auth, sandbox, users, controller, memor
1116
1121
  }
1117
1122
  }
1118
1123
  }),
1124
+ registerApiRoute("/web/github/projects/:id/commits", {
1125
+ method: "GET",
1126
+ requiresAuth: false,
1127
+ handler: async (c) => {
1128
+ const loaded = await loadOrgProject({
1129
+ github,
1130
+ auth,
1131
+ c: loose(c)
1132
+ });
1133
+ if ("response" in loaded) return loaded.response;
1134
+ const { orgId, project } = loaded;
1135
+ const branch = c.req.query("branch") ?? project.defaultBranch;
1136
+ if (!isValidGitRef$1(branch)) return c.json({ error: "Invalid branch" }, 400);
1137
+ const limit = Math.min(Math.max(Math.floor(Number(c.req.query("limit") ?? DEFAULT_COMMIT_PAGE)) || DEFAULT_COMMIT_PAGE, 1), MAX_COMMIT_PAGE);
1138
+ try {
1139
+ const commits = await listRepositoryCommits(github, {
1140
+ orgId,
1141
+ project,
1142
+ branch,
1143
+ limit
1144
+ });
1145
+ return c.json({
1146
+ commits,
1147
+ branch
1148
+ });
1149
+ } catch (err) {
1150
+ return gitErrorResponse(loose(c), err);
1151
+ }
1152
+ }
1153
+ }),
1119
1154
  registerApiRoute("/web/github/projects/:id/push", {
1120
1155
  method: "POST",
1121
1156
  requiresAuth: false,