@mastra/factory 0.9.0 → 0.10.0-alpha.10

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 (129) hide show
  1. package/CHANGELOG.md +179 -0
  2. package/dist/auth.d.ts.map +1 -1
  3. package/dist/auth.js +1 -1
  4. package/dist/auth.js.map +1 -1
  5. package/dist/factory.d.ts +8 -0
  6. package/dist/factory.d.ts.map +1 -1
  7. package/dist/factory.js +24 -8
  8. package/dist/factory.js.map +1 -1
  9. package/dist/index.d.ts +2 -0
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +2 -1
  12. package/dist/integrations/github/integration.d.ts.map +1 -1
  13. package/dist/integrations/github/integration.js +1 -0
  14. package/dist/integrations/github/integration.js.map +1 -1
  15. package/dist/integrations/github/provenance.d.ts +7 -2
  16. package/dist/integrations/github/provenance.d.ts.map +1 -1
  17. package/dist/integrations/github/provenance.js +16 -2
  18. package/dist/integrations/github/provenance.js.map +1 -1
  19. package/dist/integrations/github/routes.d.ts +3 -0
  20. package/dist/integrations/github/routes.d.ts.map +1 -1
  21. package/dist/integrations/github/routes.js +165 -6
  22. package/dist/integrations/github/routes.js.map +1 -1
  23. package/dist/integrations/github/rules.d.ts.map +1 -1
  24. package/dist/integrations/github/rules.js +193 -127
  25. package/dist/integrations/github/rules.js.map +1 -1
  26. package/dist/integrations/linear/routes.d.ts.map +1 -1
  27. package/dist/integrations/linear/routes.js +50 -0
  28. package/dist/integrations/linear/routes.js.map +1 -1
  29. package/dist/integrations/platform/github/integration.d.ts.map +1 -1
  30. package/dist/integrations/platform/github/integration.js +1 -0
  31. package/dist/integrations/platform/github/integration.js.map +1 -1
  32. package/dist/routes/attention.d.ts +17 -0
  33. package/dist/routes/attention.d.ts.map +1 -0
  34. package/dist/routes/attention.js +305 -0
  35. package/dist/routes/attention.js.map +1 -0
  36. package/dist/routes/intake.d.ts.map +1 -1
  37. package/dist/routes/intake.js +11 -2
  38. package/dist/routes/intake.js.map +1 -1
  39. package/dist/routes/surface.d.ts +2 -3
  40. package/dist/routes/surface.d.ts.map +1 -1
  41. package/dist/routes/surface.js +61 -43
  42. package/dist/routes/surface.js.map +1 -1
  43. package/dist/routes/work-items.d.ts.map +1 -1
  44. package/dist/routes/work-items.js +35 -14
  45. package/dist/routes/work-items.js.map +1 -1
  46. package/dist/rules/defaults.js +5 -5
  47. package/dist/rules/defaults.js.map +1 -1
  48. package/dist/rules/dispatch-errors.d.ts +13 -0
  49. package/dist/rules/dispatch-errors.d.ts.map +1 -0
  50. package/dist/rules/dispatch-errors.js +77 -0
  51. package/dist/rules/dispatch-errors.js.map +1 -0
  52. package/dist/rules/dispatcher.d.ts +9 -0
  53. package/dist/rules/dispatcher.d.ts.map +1 -1
  54. package/dist/rules/dispatcher.js +118 -45
  55. package/dist/rules/dispatcher.js.map +1 -1
  56. package/dist/rules/processor.d.ts.map +1 -1
  57. package/dist/rules/processor.js +30 -14
  58. package/dist/rules/processor.js.map +1 -1
  59. package/dist/rules/terminal-cleanup.d.ts +1 -1
  60. package/dist/rules/terminal-cleanup.d.ts.map +1 -1
  61. package/dist/rules/terminal-cleanup.js +2 -2
  62. package/dist/rules/terminal-cleanup.js.map +1 -1
  63. package/dist/rules/transition-service.d.ts +2 -1
  64. package/dist/rules/transition-service.d.ts.map +1 -1
  65. package/dist/rules/transition-service.js +1 -1
  66. package/dist/rules/transition-service.js.map +1 -1
  67. package/dist/rules/types.d.ts +2 -0
  68. package/dist/rules/types.d.ts.map +1 -1
  69. package/dist/rules/types.js +9 -1
  70. package/dist/rules/types.js.map +1 -1
  71. package/dist/secret-encryption.d.ts +25 -0
  72. package/dist/secret-encryption.d.ts.map +1 -0
  73. package/dist/secret-encryption.js +96 -0
  74. package/dist/secret-encryption.js.map +1 -0
  75. package/dist/session/factory-session.d.ts +13 -3
  76. package/dist/session/factory-session.d.ts.map +1 -1
  77. package/dist/session/factory-session.js +15 -6
  78. package/dist/session/factory-session.js.map +1 -1
  79. package/dist/session/first-exec-capture.d.ts +16 -10
  80. package/dist/session/first-exec-capture.d.ts.map +1 -1
  81. package/dist/session/first-exec-capture.js +74 -14
  82. package/dist/session/first-exec-capture.js.map +1 -1
  83. package/dist/session/first-message-capture.d.ts +14 -10
  84. package/dist/session/first-message-capture.d.ts.map +1 -1
  85. package/dist/session/first-message-capture.js +17 -11
  86. package/dist/session/first-message-capture.js.map +1 -1
  87. package/dist/session/session-title.d.ts +7 -0
  88. package/dist/session/session-title.d.ts.map +1 -0
  89. package/dist/session/session-title.js +14 -0
  90. package/dist/session/session-title.js.map +1 -0
  91. package/dist/session/thread-title-mirror.d.ts +35 -0
  92. package/dist/session/thread-title-mirror.d.ts.map +1 -0
  93. package/dist/session/thread-title-mirror.js +51 -0
  94. package/dist/session/thread-title-mirror.js.map +1 -0
  95. package/dist/storage/domains/credentials/base.d.ts +3 -1
  96. package/dist/storage/domains/credentials/base.d.ts.map +1 -1
  97. package/dist/storage/domains/credentials/base.js +33 -15
  98. package/dist/storage/domains/credentials/base.js.map +1 -1
  99. package/dist/storage/domains/custom-providers/base.d.ts +3 -1
  100. package/dist/storage/domains/custom-providers/base.d.ts.map +1 -1
  101. package/dist/storage/domains/custom-providers/base.js +45 -25
  102. package/dist/storage/domains/custom-providers/base.js.map +1 -1
  103. package/dist/storage/domains/intake/base.d.ts +6 -5
  104. package/dist/storage/domains/intake/base.d.ts.map +1 -1
  105. package/dist/storage/domains/intake/base.js +21 -13
  106. package/dist/storage/domains/intake/base.js.map +1 -1
  107. package/dist/storage/domains/integrations/base.d.ts +3 -1
  108. package/dist/storage/domains/integrations/base.d.ts.map +1 -1
  109. package/dist/storage/domains/integrations/base.js +28 -12
  110. package/dist/storage/domains/integrations/base.js.map +1 -1
  111. package/dist/storage/domains/source-control/base.d.ts +8 -0
  112. package/dist/storage/domains/source-control/base.d.ts.map +1 -1
  113. package/dist/storage/domains/source-control/base.js +14 -2
  114. package/dist/storage/domains/source-control/base.js.map +1 -1
  115. package/dist/storage/domains/source-control/inmemory.d.ts +4 -0
  116. package/dist/storage/domains/source-control/inmemory.d.ts.map +1 -1
  117. package/dist/storage/domains/source-control/inmemory.js +7 -0
  118. package/dist/storage/domains/source-control/inmemory.js.map +1 -1
  119. package/dist/storage/domains/work-items/base.d.ts +115 -12
  120. package/dist/storage/domains/work-items/base.d.ts.map +1 -1
  121. package/dist/storage/domains/work-items/base.js +497 -43
  122. package/dist/storage/domains/work-items/base.js.map +1 -1
  123. package/dist/work-item-branch.d.ts +23 -0
  124. package/dist/work-item-branch.d.ts.map +1 -0
  125. package/dist/work-item-branch.js +39 -0
  126. package/dist/work-item-branch.js.map +1 -0
  127. package/factory-skills/factory-rereview/SKILL.md +2 -0
  128. package/factory-skills/factory-review/SKILL.md +2 -0
  129. package/package.json +7 -7
@@ -3,12 +3,15 @@ import { listPullRequestSubscriptionsForThread, subscribeToPullRequest } from ".
3
3
  import { SandboxBudgetError } from "../../sandbox/fleet.js";
4
4
  import { MaterializeError, WorktreeError, commitAll, ensureProjectSandbox, isValidGitRef as isValidGitRef$1, materializeRepo, pushBranch, teardownProjectSandbox } from "./sandbox.js";
5
5
  import { baseCheckpointIsStale } from "../../sandbox/base-checkpoint-triggers.js";
6
+ import { normalizeSessionTitle } from "../../session/session-title.js";
6
7
  import { getGithubFeatureDiagnostics, isGithubFeatureEnabled } from "./config.js";
7
8
  import { reclaimDeletedSessionSandbox } from "./sandbox-release.js";
8
9
  import { handleGithubWebhook } from "./webhook.js";
9
10
  import { registerApiRoute } from "@mastra/core/server";
10
11
  import { randomUUID } from "crypto";
11
12
  import { UniqueViolationError } from "@mastra/core/storage";
13
+ import { resolveModel } from "@mastra/code-sdk/agents/model";
14
+ import { RequestContext } from "@mastra/core/request-context";
12
15
  import { streamSSE } from "hono/streaming";
13
16
  //#region src/integrations/github/routes.ts
14
17
  /**
@@ -24,7 +27,6 @@ import { streamSSE } from "hono/streaming";
24
27
  * so the SPA can cleanly hide all GitHub UI.
25
28
  */
26
29
  const sessionOperationLocks = /* @__PURE__ */ new Map();
27
- const MAX_SESSION_TITLE_LENGTH = 80;
28
30
  const USER_SESSION_BRANCH_PREFIX = "user/session-";
29
31
  const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
30
32
  /**
@@ -69,9 +71,6 @@ function isValidGitRef(value) {
69
71
  function isJsonObject(value) {
70
72
  return typeof value === "object" && value !== null && !Array.isArray(value);
71
73
  }
72
- function normalizeSessionTitle(title) {
73
- return [...title.replace(/\s+/g, " ").trim()].slice(0, MAX_SESSION_TITLE_LENGTH).join("").trimEnd() || null;
74
- }
75
74
  /**
76
75
  * Resolve the org-scoped tenant for a GitHub request. GitHub project features
77
76
  * are org-owned, so they require both a signed-in user and a WorkOS
@@ -108,6 +107,11 @@ function parseListPage(raw) {
108
107
  const page = Number(raw);
109
108
  return page >= 1 ? page : null;
110
109
  }
110
+ function parseResourceNumber(raw) {
111
+ if (raw === void 0 || !/^\d{1,10}$/.test(raw)) return null;
112
+ const parsed = Number(raw);
113
+ return parsed > 0 ? parsed : null;
114
+ }
111
115
  const VALID_ISSUE_LABEL_FILTERS = /* @__PURE__ */ new Set(["status: auto-triaged", "status: needs approval"]);
112
116
  function parseIssueLabelFilter(raw) {
113
117
  if (raw === void 0 || raw === "") return void 0;
@@ -208,7 +212,7 @@ async function ingestPolledEvents(events, ingestFactoryEvent) {
208
212
  */
209
213
  function buildGithubRoutes(options) {
210
214
  const routes = [];
211
- const { auth, fleet, storage, github, stateSigner, controller, emitAudit, sessionRetirement } = options;
215
+ const { auth, fleet, storage, github, stateSigner, controller, memorySettings, emitAudit, sessionRetirement } = options;
212
216
  const diagnostics = () => getGithubFeatureDiagnostics({
213
217
  github,
214
218
  auth,
@@ -531,6 +535,48 @@ function buildGithubRoutes(options) {
531
535
  }
532
536
  }
533
537
  }));
538
+ routes.push(registerApiRoute("/web/github/projects/:id/issues/:number", {
539
+ method: "GET",
540
+ requiresAuth: false,
541
+ handler: async (c) => {
542
+ const loaded = await loadOrgProject({
543
+ github,
544
+ auth,
545
+ c: loose(c)
546
+ });
547
+ if ("response" in loaded) return loaded.response;
548
+ const number = parseResourceNumber(c.req.param("number"));
549
+ if (number === null) return c.json({ error: "invalid_number" }, 400);
550
+ try {
551
+ const detail = await github.intake.getIssue({
552
+ connection: {
553
+ type: "app-installation",
554
+ installationId: Number(loaded.project.installation.externalId)
555
+ },
556
+ sourceId: loaded.project.repository.slug,
557
+ issueId: String(number)
558
+ });
559
+ if (detail === null) return c.json({ error: "issue_not_found" }, 404);
560
+ return c.json({
561
+ number: Number(detail.id),
562
+ title: detail.title,
563
+ url: detail.url,
564
+ author: detail.author,
565
+ assignee: detail.assignee,
566
+ labels: detail.labels,
567
+ comments: detail.commentCount ?? 0,
568
+ createdAt: detail.createdAt,
569
+ updatedAt: detail.updatedAt,
570
+ description: detail.description
571
+ });
572
+ } catch (err) {
573
+ return c.json({
574
+ error: "github_fetch_failed",
575
+ message: err instanceof Error ? err.message : String(err)
576
+ }, 502);
577
+ }
578
+ }
579
+ }));
534
580
  routes.push(registerApiRoute("/web/github/projects/:id/prs", {
535
581
  method: "GET",
536
582
  requiresAuth: false,
@@ -578,6 +624,49 @@ function buildGithubRoutes(options) {
578
624
  }
579
625
  }
580
626
  }));
627
+ routes.push(registerApiRoute("/web/github/projects/:id/prs/:number", {
628
+ method: "GET",
629
+ requiresAuth: false,
630
+ handler: async (c) => {
631
+ const loaded = await loadOrgProject({
632
+ github,
633
+ auth,
634
+ c: loose(c)
635
+ });
636
+ if ("response" in loaded) return loaded.response;
637
+ const number = parseResourceNumber(c.req.param("number"));
638
+ if (number === null) return c.json({ error: "invalid_number" }, 400);
639
+ try {
640
+ const pr = await github.versionControl.getPullRequest({
641
+ connection: {
642
+ type: "app-installation",
643
+ installationId: Number(loaded.project.installation.externalId)
644
+ },
645
+ sourceId: loaded.project.repository.slug,
646
+ pullRequestId: String(number)
647
+ });
648
+ if (pr === null) return c.json({ error: "pull_request_not_found" }, 404);
649
+ return c.json({
650
+ number: Number(pr.id),
651
+ title: pr.title,
652
+ url: pr.url,
653
+ author: pr.author,
654
+ assignees: pr.assignees ?? [],
655
+ requestedReviewers: pr.requestedReviewers ?? [],
656
+ baseBranch: pr.baseBranch,
657
+ headBranch: pr.headBranch,
658
+ createdAt: pr.createdAt,
659
+ updatedAt: pr.updatedAt,
660
+ description: pr.body
661
+ });
662
+ } catch (err) {
663
+ return c.json({
664
+ error: "github_fetch_failed",
665
+ message: err instanceof Error ? err.message : String(err)
666
+ }, 502);
667
+ }
668
+ }
669
+ }));
581
670
  routes.push(registerApiRoute("/web/github/projects/:id/settings", {
582
671
  method: "GET",
583
672
  requiresAuth: false,
@@ -688,6 +777,7 @@ function buildGithubRoutes(options) {
688
777
  auth,
689
778
  fleet,
690
779
  controller,
780
+ memorySettings,
691
781
  emitAudit,
692
782
  sessionRetirement
693
783
  }));
@@ -894,7 +984,33 @@ async function loadOwnedProject(options) {
894
984
  sandboxRow: await loadOrCreateSandboxRow(github, project, userId)
895
985
  };
896
986
  }
897
- function buildProjectGitRoutes({ github, auth, fleet, controller, emitAudit, sessionRetirement }) {
987
+ /**
988
+ * One naming per session at a time: a second caller joins the run already in
989
+ * flight instead of paying for another model call and racing its rename.
990
+ */
991
+ function createSessionNaming() {
992
+ const inFlight = /* @__PURE__ */ new Map();
993
+ return (sessionId, run) => {
994
+ const pending = inFlight.get(sessionId);
995
+ if (pending) return pending;
996
+ const started = run().finally(() => inFlight.delete(sessionId));
997
+ inFlight.set(sessionId, started);
998
+ return started;
999
+ };
1000
+ }
1001
+ /**
1002
+ * Name a thread with a stored model id. Resolution has to go through
1003
+ * mastracode's gateway: a bare id handed to core's model router looks for a
1004
+ * process env key instead of the caller's stored provider credentials.
1005
+ */
1006
+ function titleModel(modelId) {
1007
+ return ({ requestContext }) => resolveModel(modelId, {
1008
+ remapForCodexOAuth: true,
1009
+ requestContext
1010
+ });
1011
+ }
1012
+ function buildProjectGitRoutes({ github, auth, fleet, controller, memorySettings, emitAudit, sessionRetirement }) {
1013
+ const nameSession = createSessionNaming();
898
1014
  return [
899
1015
  registerApiRoute("/web/github/projects/:id/sessions", {
900
1016
  method: "GET",
@@ -1029,6 +1145,49 @@ function buildProjectGitRoutes({ github, auth, fleet, controller, emitAudit, ses
1029
1145
  return c.json({ removed: true });
1030
1146
  }
1031
1147
  }),
1148
+ registerApiRoute("/web/user-sessions/:sessionId/title", {
1149
+ method: "POST",
1150
+ requiresAuth: false,
1151
+ handler: async (c) => {
1152
+ const resolved = await resolveOrgTenant(loose(c), auth);
1153
+ if ("response" in resolved) return resolved.response;
1154
+ const sessionId = c.req.param("sessionId");
1155
+ const row = await github.sourceControlStorage.sessions.getBySessionId(sessionId);
1156
+ if (!row || row.orgId !== resolved.tenant.orgId || row.userId !== resolved.tenant.userId) return c.json({ error: "Session not found" }, 404);
1157
+ if (!controller) return c.json({ error: "Sessions are not available on this server." }, 503);
1158
+ const thread = (await controller.queryThreads({ resourceId: sessionId })).sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime())[0];
1159
+ if (!thread) return c.json({ error: "This session has no conversation to name yet." }, 409);
1160
+ const stored = await memorySettings.get({
1161
+ orgId: row.orgId,
1162
+ userId: row.userId
1163
+ });
1164
+ const requestContext = new RequestContext();
1165
+ requestContext.set("user", {
1166
+ workosId: row.userId,
1167
+ organizationId: row.orgId
1168
+ });
1169
+ try {
1170
+ const title = await nameSession(sessionId, async () => {
1171
+ const generated = await controller.generateThreadTitle({
1172
+ threadId: thread.id,
1173
+ resourceId: sessionId,
1174
+ requestContext,
1175
+ ...stored?.observerModelId ? { model: titleModel(stored.observerModelId) } : {}
1176
+ });
1177
+ const named = generated ? normalizeSessionTitle(generated) : null;
1178
+ if (named) await github.sourceControlStorage.sessions.rename({
1179
+ sessionId,
1180
+ title: named
1181
+ });
1182
+ return named;
1183
+ });
1184
+ if (!title) return c.json({ error: "The model returned an empty title. Try again." }, 502);
1185
+ return c.json({ title });
1186
+ } catch (error) {
1187
+ return c.json({ error: error instanceof Error ? error.message : String(error) }, 500);
1188
+ }
1189
+ }
1190
+ }),
1032
1191
  registerApiRoute("/web/github/projects/:id/commit", {
1033
1192
  method: "POST",
1034
1193
  requiresAuth: false,