@mastra/factory 0.5.1-alpha.0 → 0.6.0-alpha.3

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 (154) hide show
  1. package/CHANGELOG.md +151 -0
  2. package/dist/auth.d.ts +2 -0
  3. package/dist/auth.d.ts.map +1 -1
  4. package/dist/auth.js +2 -0
  5. package/dist/auth.js.map +1 -1
  6. package/dist/capabilities/intake.d.ts +1 -0
  7. package/dist/capabilities/intake.d.ts.map +1 -1
  8. package/dist/capabilities/version-control.d.ts +3 -0
  9. package/dist/capabilities/version-control.d.ts.map +1 -1
  10. package/dist/factory.d.ts +10 -0
  11. package/dist/factory.d.ts.map +1 -1
  12. package/dist/factory.js +15 -2
  13. package/dist/factory.js.map +1 -1
  14. package/dist/integrations/base.d.ts +6 -0
  15. package/dist/integrations/base.d.ts.map +1 -1
  16. package/dist/integrations/github/integration.d.ts +14 -1
  17. package/dist/integrations/github/integration.d.ts.map +1 -1
  18. package/dist/integrations/github/integration.js +61 -3
  19. package/dist/integrations/github/integration.js.map +1 -1
  20. package/dist/integrations/github/issue-reconciler.d.ts +24 -0
  21. package/dist/integrations/github/issue-reconciler.d.ts.map +1 -0
  22. package/dist/integrations/github/issue-reconciler.js +104 -0
  23. package/dist/integrations/github/issue-reconciler.js.map +1 -0
  24. package/dist/integrations/github/reconcile-worker.d.ts +8 -0
  25. package/dist/integrations/github/reconcile-worker.d.ts.map +1 -1
  26. package/dist/integrations/github/reconcile-worker.js +38 -1
  27. package/dist/integrations/github/reconcile-worker.js.map +1 -1
  28. package/dist/integrations/github/routes.d.ts.map +1 -1
  29. package/dist/integrations/github/routes.js +65 -52
  30. package/dist/integrations/github/routes.js.map +1 -1
  31. package/dist/integrations/github/rules.d.ts +37 -2
  32. package/dist/integrations/github/rules.d.ts.map +1 -1
  33. package/dist/integrations/github/rules.js +150 -14
  34. package/dist/integrations/github/rules.js.map +1 -1
  35. package/dist/integrations/github/sandbox-release.d.ts +16 -1
  36. package/dist/integrations/github/sandbox-release.d.ts.map +1 -1
  37. package/dist/integrations/github/sandbox-release.js +42 -1
  38. package/dist/integrations/github/sandbox-release.js.map +1 -1
  39. package/dist/integrations/github/sandbox.d.ts.map +1 -1
  40. package/dist/integrations/github/sandbox.js +74 -8
  41. package/dist/integrations/github/sandbox.js.map +1 -1
  42. package/dist/integrations/issue-reconcile-worker.d.ts +19 -0
  43. package/dist/integrations/issue-reconcile-worker.d.ts.map +1 -0
  44. package/dist/integrations/issue-reconcile-worker.js +98 -0
  45. package/dist/integrations/issue-reconcile-worker.js.map +1 -0
  46. package/dist/integrations/issue-reconciler.d.ts +47 -0
  47. package/dist/integrations/issue-reconciler.d.ts.map +1 -0
  48. package/dist/integrations/issue-reconciler.js +104 -0
  49. package/dist/integrations/issue-reconciler.js.map +1 -0
  50. package/dist/integrations/linear/integration.d.ts +4 -0
  51. package/dist/integrations/linear/integration.d.ts.map +1 -1
  52. package/dist/integrations/linear/integration.js +19 -2
  53. package/dist/integrations/linear/integration.js.map +1 -1
  54. package/dist/integrations/linear/issue-reconciler.d.ts +6 -0
  55. package/dist/integrations/linear/issue-reconciler.d.ts.map +1 -0
  56. package/dist/integrations/linear/issue-reconciler.js +64 -0
  57. package/dist/integrations/linear/issue-reconciler.js.map +1 -0
  58. package/dist/integrations/linear/routes.d.ts.map +1 -1
  59. package/dist/integrations/linear/routes.js +1 -0
  60. package/dist/integrations/linear/routes.js.map +1 -1
  61. package/dist/integrations/linear/rules.d.ts +2 -0
  62. package/dist/integrations/linear/rules.d.ts.map +1 -1
  63. package/dist/integrations/linear/rules.js +5 -2
  64. package/dist/integrations/linear/rules.js.map +1 -1
  65. package/dist/integrations/platform/github/event-worker.d.ts +7 -0
  66. package/dist/integrations/platform/github/event-worker.d.ts.map +1 -1
  67. package/dist/integrations/platform/github/event-worker.js +24 -0
  68. package/dist/integrations/platform/github/event-worker.js.map +1 -1
  69. package/dist/integrations/platform/github/integration.d.ts +13 -3
  70. package/dist/integrations/platform/github/integration.d.ts.map +1 -1
  71. package/dist/integrations/platform/github/integration.js +42 -1
  72. package/dist/integrations/platform/github/integration.js.map +1 -1
  73. package/dist/integrations/platform/linear/event-worker.d.ts +105 -0
  74. package/dist/integrations/platform/linear/event-worker.d.ts.map +1 -0
  75. package/dist/integrations/platform/linear/event-worker.js +380 -0
  76. package/dist/integrations/platform/linear/event-worker.js.map +1 -0
  77. package/dist/integrations/platform/linear/integration.d.ts +9 -0
  78. package/dist/integrations/platform/linear/integration.d.ts.map +1 -1
  79. package/dist/integrations/platform/linear/integration.js +37 -1
  80. package/dist/integrations/platform/linear/integration.js.map +1 -1
  81. package/dist/integrations/slack/integration.d.ts.map +1 -1
  82. package/dist/integrations/slack/integration.js +3 -2
  83. package/dist/integrations/slack/integration.js.map +1 -1
  84. package/dist/integrations/slack/slack.d.ts +37 -110
  85. package/dist/integrations/slack/slack.d.ts.map +1 -1
  86. package/dist/integrations/slack/slack.js +49 -44
  87. package/dist/integrations/slack/slack.js.map +1 -1
  88. package/dist/routes/config.d.ts +25 -0
  89. package/dist/routes/config.d.ts.map +1 -1
  90. package/dist/routes/config.js +63 -1
  91. package/dist/routes/config.js.map +1 -1
  92. package/dist/routes/fs.d.ts +16 -2
  93. package/dist/routes/fs.d.ts.map +1 -1
  94. package/dist/routes/fs.js +47 -5
  95. package/dist/routes/fs.js.map +1 -1
  96. package/dist/routes/surface.d.ts +13 -1
  97. package/dist/routes/surface.d.ts.map +1 -1
  98. package/dist/routes/surface.js +20 -9
  99. package/dist/routes/surface.js.map +1 -1
  100. package/dist/rules/binding-context.d.ts +28 -1
  101. package/dist/rules/binding-context.d.ts.map +1 -1
  102. package/dist/rules/binding-context.js +66 -1
  103. package/dist/rules/binding-context.js.map +1 -1
  104. package/dist/rules/defaults.d.ts.map +1 -1
  105. package/dist/rules/defaults.js +71 -5
  106. package/dist/rules/defaults.js.map +1 -1
  107. package/dist/rules/start-coordinator.d.ts.map +1 -1
  108. package/dist/rules/start-coordinator.js +7 -26
  109. package/dist/rules/start-coordinator.js.map +1 -1
  110. package/dist/rules/tools.d.ts +2 -0
  111. package/dist/rules/tools.d.ts.map +1 -1
  112. package/dist/rules/tools.js +15 -6
  113. package/dist/rules/tools.js.map +1 -1
  114. package/dist/rules/types.d.ts +17 -2
  115. package/dist/rules/types.d.ts.map +1 -1
  116. package/dist/rules/types.js +2 -1
  117. package/dist/rules/types.js.map +1 -1
  118. package/dist/session/factory-session.d.ts +116 -0
  119. package/dist/session/factory-session.d.ts.map +1 -0
  120. package/dist/session/factory-session.js +162 -0
  121. package/dist/session/factory-session.js.map +1 -0
  122. package/dist/session/filesystem-capture.d.ts +26 -0
  123. package/dist/session/filesystem-capture.d.ts.map +1 -0
  124. package/dist/session/filesystem-capture.js +75 -0
  125. package/dist/session/filesystem-capture.js.map +1 -0
  126. package/dist/storage/domains/audit/domain.d.ts +9 -2
  127. package/dist/storage/domains/audit/domain.d.ts.map +1 -1
  128. package/dist/storage/domains/audit/domain.js +106 -4
  129. package/dist/storage/domains/audit/domain.js.map +1 -1
  130. package/dist/storage/domains/filesystem/base.d.ts +26 -0
  131. package/dist/storage/domains/filesystem/base.d.ts.map +1 -0
  132. package/dist/storage/domains/filesystem/base.js +74 -0
  133. package/dist/storage/domains/filesystem/base.js.map +1 -0
  134. package/dist/storage/domains/projects/base.d.ts +1 -0
  135. package/dist/storage/domains/projects/base.d.ts.map +1 -1
  136. package/dist/storage/domains/projects/base.js +3 -0
  137. package/dist/storage/domains/projects/base.js.map +1 -1
  138. package/dist/storage/domains/source-control/base.d.ts +2 -0
  139. package/dist/storage/domains/source-control/base.d.ts.map +1 -1
  140. package/dist/storage/domains/source-control/base.js +6 -0
  141. package/dist/storage/domains/source-control/base.js.map +1 -1
  142. package/dist/storage/domains/source-control/inmemory.d.ts.map +1 -1
  143. package/dist/storage/domains/source-control/inmemory.js +3 -0
  144. package/dist/storage/domains/source-control/inmemory.js.map +1 -1
  145. package/dist/storage/domains/work-items/base.d.ts +11 -0
  146. package/dist/storage/domains/work-items/base.d.ts.map +1 -1
  147. package/dist/storage/domains/work-items/base.js +28 -0
  148. package/dist/storage/domains/work-items/base.js.map +1 -1
  149. package/factory-skills/factory-triage/SKILL.md +45 -12
  150. package/package.json +5 -5
  151. package/dist/integrations/github/factory-session.d.ts +0 -12
  152. package/dist/integrations/github/factory-session.d.ts.map +0 -1
  153. package/dist/integrations/github/factory-session.js +0 -37
  154. package/dist/integrations/github/factory-session.js.map +0 -1
@@ -22,7 +22,72 @@ function getFactorySessionAddress(requestContext) {
22
22
  ...coordinates
23
23
  };
24
24
  }
25
+ /**
26
+ * Resolve the bound session address, falling back to the active binding row
27
+ * when the in-memory session state is missing `factoryProjectId` — a server
28
+ * crash drops controller sessions, and a resumed session ("Continue") is
29
+ * recreated with empty state, which silently unregistered the transition
30
+ * tool. On recovery the lost state is healed back onto the session (including
31
+ * the `untrustedCheckout`/`baseRef` security posture the start coordinator
32
+ * originally seeded) so later reads resolve directly.
33
+ */
34
+ async function resolveFactorySessionAddress(options) {
35
+ const direct = getFactorySessionAddress(options.requestContext);
36
+ if (direct) return { address: direct };
37
+ const requestContext = options.requestContext;
38
+ if (!requestContext || typeof requestContext.get !== "function") return null;
39
+ const context = requestContext.get("controller");
40
+ if (!context?.threadId || !context.resourceId) return null;
41
+ const orgId = getFactoryAuthOrgId(requestContext.get("user"));
42
+ if (!orgId) return null;
43
+ const binding = await options.storage.findActiveRunBindingByThread({
44
+ orgId,
45
+ threadId: context.threadId,
46
+ resourceId: context.resourceId,
47
+ sessionId: context.resourceId
48
+ });
49
+ if (!binding) return null;
50
+ await healRecoveredSessionState({
51
+ context,
52
+ binding,
53
+ storage: options.storage,
54
+ sessions: options.sessions
55
+ });
56
+ return {
57
+ address: {
58
+ orgId,
59
+ factoryProjectId: binding.factoryProjectId,
60
+ threadId: context.threadId,
61
+ resourceId: context.resourceId,
62
+ sessionId: context.resourceId
63
+ },
64
+ binding
65
+ };
66
+ }
67
+ async function healRecoveredSessionState(options) {
68
+ const { binding } = options;
69
+ const updates = { factoryProjectId: binding.factoryProjectId };
70
+ if (binding.role === "review") updates.untrustedCheckout = true;
71
+ try {
72
+ const item = await options.storage.get({
73
+ orgId: binding.orgId,
74
+ id: binding.workItemId
75
+ });
76
+ const foundSession = await options.sessions?.getBySessionId(binding.sessionId) ?? null;
77
+ const sourceSession = foundSession?.orgId === binding.orgId ? foundSession : null;
78
+ if (sourceSession) updates.projectRepositoryId = sourceSession.projectRepositoryId;
79
+ if (updates.untrustedCheckout === true || item?.externalSource?.type === "pull-request") {
80
+ updates.untrustedCheckout = true;
81
+ const metadataBaseBranch = item?.metadata?.baseBranch;
82
+ const baseRef = sourceSession?.baseBranch || (typeof metadataBaseBranch === "string" && metadataBaseBranch ? metadataBaseBranch : void 0);
83
+ if (baseRef) updates.baseRef = baseRef;
84
+ }
85
+ } catch {}
86
+ try {
87
+ await options.context.setState(updates);
88
+ } catch {}
89
+ }
25
90
  //#endregion
26
- export { getFactorySessionAddress, getFactorySessionCoordinates };
91
+ export { getFactorySessionAddress, getFactorySessionCoordinates, resolveFactorySessionAddress };
27
92
 
28
93
  //# sourceMappingURL=binding-context.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"binding-context.js","names":[],"sources":["../../src/rules/binding-context.ts"],"sourcesContent":["import type { AgentControllerRequestContext } from '@mastra/core/agent-controller';\nimport type { RequestContext } from '@mastra/core/request-context';\n\nimport type { FactoryAuthUser } from '../auth.js';\nimport { getFactoryAuthOrgId } from '../auth.js';\nimport type { FactoryRunBindingAddress, FactoryRunBindingSessionAddress } from '../storage/domains/work-items/base.js';\n\ninterface FactorySessionState {\n factoryProjectId?: string;\n}\n\nexport function getFactorySessionCoordinates(\n requestContext: RequestContext | undefined,\n): FactoryRunBindingSessionAddress | null {\n if (!requestContext || typeof requestContext.get !== 'function') return null;\n const context = requestContext.get('controller') as AgentControllerRequestContext<FactorySessionState> | undefined;\n const factoryProjectId = context?.getState().factoryProjectId;\n if (!context?.threadId || !context.resourceId || !factoryProjectId) return null;\n return {\n factoryProjectId,\n threadId: context.threadId,\n resourceId: context.resourceId,\n sessionId: context.resourceId,\n };\n}\n\nexport function getFactorySessionAddress(requestContext: RequestContext | undefined): FactoryRunBindingAddress | null {\n const coordinates = getFactorySessionCoordinates(requestContext);\n if (!coordinates || !requestContext || typeof requestContext.get !== 'function') return null;\n const user = requestContext.get('user') as FactoryAuthUser | undefined;\n const orgId = getFactoryAuthOrgId(user);\n if (!orgId) return null;\n return { orgId, ...coordinates };\n}\n"],"mappings":";;AAWA,SAAgB,6BACd,gBACwC;CACxC,IAAI,CAAC,kBAAkB,OAAO,eAAe,QAAQ,YAAY,OAAO;CACxE,MAAM,UAAU,eAAe,IAAI,YAAY;CAC/C,MAAM,mBAAmB,SAAS,SAAS,CAAC,CAAC;CAC7C,IAAI,CAAC,SAAS,YAAY,CAAC,QAAQ,cAAc,CAAC,kBAAkB,OAAO;CAC3E,OAAO;EACL;EACA,UAAU,QAAQ;EAClB,YAAY,QAAQ;EACpB,WAAW,QAAQ;CACrB;AACF;AAEA,SAAgB,yBAAyB,gBAA6E;CACpH,MAAM,cAAc,6BAA6B,cAAc;CAC/D,IAAI,CAAC,eAAe,CAAC,kBAAkB,OAAO,eAAe,QAAQ,YAAY,OAAO;CAExF,MAAM,QAAQ,oBADD,eAAe,IAAI,MACK,CAAC;CACtC,IAAI,CAAC,OAAO,OAAO;CACnB,OAAO;EAAE;EAAO,GAAG;CAAY;AACjC"}
1
+ {"version":3,"file":"binding-context.js","names":[],"sources":["../../src/rules/binding-context.ts"],"sourcesContent":["import type { AgentControllerRequestContext } from '@mastra/core/agent-controller';\nimport type { RequestContext } from '@mastra/core/request-context';\n\nimport type { FactoryAuthUser } from '../auth.js';\nimport { getFactoryAuthOrgId } from '../auth.js';\nimport type {\n FactoryRunBindingAddress,\n FactoryRunBindingRecord,\n FactoryRunBindingSessionAddress,\n WorkItemsStorage,\n} from '../storage/domains/work-items/base.js';\n\ninterface FactorySessionState {\n factoryProjectId?: string;\n projectRepositoryId?: string;\n untrustedCheckout?: boolean;\n baseRef?: string;\n}\n\ntype FactorySessionControllerContext = AgentControllerRequestContext<FactorySessionState>;\n\n/** Minimal source-control session lookup used to heal recovered session state. */\nexport interface FactorySessionSourceLookup {\n getBySessionId(sessionId: string): Promise<{ orgId: string; projectRepositoryId: string; baseBranch: string } | null>;\n}\n\nexport function getFactorySessionCoordinates(\n requestContext: RequestContext | undefined,\n): FactoryRunBindingSessionAddress | null {\n if (!requestContext || typeof requestContext.get !== 'function') return null;\n const context = requestContext.get('controller') as FactorySessionControllerContext | undefined;\n const factoryProjectId = context?.getState().factoryProjectId;\n if (!context?.threadId || !context.resourceId || !factoryProjectId) return null;\n return {\n factoryProjectId,\n threadId: context.threadId,\n resourceId: context.resourceId,\n sessionId: context.resourceId,\n };\n}\n\nexport function getFactorySessionAddress(requestContext: RequestContext | undefined): FactoryRunBindingAddress | null {\n const coordinates = getFactorySessionCoordinates(requestContext);\n if (!coordinates || !requestContext || typeof requestContext.get !== 'function') return null;\n const user = requestContext.get('user') as FactoryAuthUser | undefined;\n const orgId = getFactoryAuthOrgId(user);\n if (!orgId) return null;\n return { orgId, ...coordinates };\n}\n\nexport interface FactorySessionAddressResolution {\n address: FactoryRunBindingAddress;\n /** Present only when the address was recovered from the binding table. */\n binding?: FactoryRunBindingRecord;\n}\n\n/**\n * Resolve the bound session address, falling back to the active binding row\n * when the in-memory session state is missing `factoryProjectId` — a server\n * crash drops controller sessions, and a resumed session (\"Continue\") is\n * recreated with empty state, which silently unregistered the transition\n * tool. On recovery the lost state is healed back onto the session (including\n * the `untrustedCheckout`/`baseRef` security posture the start coordinator\n * originally seeded) so later reads resolve directly.\n */\nexport async function resolveFactorySessionAddress(options: {\n requestContext: RequestContext | undefined;\n storage: Pick<WorkItemsStorage, 'findActiveRunBindingByThread' | 'get'>;\n sessions?: FactorySessionSourceLookup;\n}): Promise<FactorySessionAddressResolution | null> {\n const direct = getFactorySessionAddress(options.requestContext);\n if (direct) return { address: direct };\n\n const requestContext = options.requestContext;\n if (!requestContext || typeof requestContext.get !== 'function') return null;\n const context = requestContext.get('controller') as FactorySessionControllerContext | undefined;\n if (!context?.threadId || !context.resourceId) return null;\n const user = requestContext.get('user') as FactoryAuthUser | undefined;\n const orgId = getFactoryAuthOrgId(user);\n if (!orgId) return null;\n\n const binding = await options.storage.findActiveRunBindingByThread({\n orgId,\n threadId: context.threadId,\n resourceId: context.resourceId,\n sessionId: context.resourceId,\n });\n if (!binding) return null;\n\n await healRecoveredSessionState({ context, binding, storage: options.storage, sessions: options.sessions });\n\n return {\n address: {\n orgId,\n factoryProjectId: binding.factoryProjectId,\n threadId: context.threadId,\n resourceId: context.resourceId,\n sessionId: context.resourceId,\n },\n binding,\n };\n}\n\nasync function healRecoveredSessionState(options: {\n context: FactorySessionControllerContext;\n binding: FactoryRunBindingRecord;\n storage: Pick<WorkItemsStorage, 'get'>;\n sessions?: FactorySessionSourceLookup;\n}): Promise<void> {\n const { binding } = options;\n const updates: FactorySessionState = { factoryProjectId: binding.factoryProjectId };\n // Security posture first, from the binding row alone (no I/O): review-bound\n // sessions run against attacker-writable checkouts, so `untrustedCheckout`\n // must survive even if the enrichment lookups below fail transiently.\n if (binding.role === 'review') {\n updates.untrustedCheckout = true;\n }\n // Best-effort enrichment mirroring the start coordinator's original seed:\n // PR-sourced work items are untrusted regardless of role, and `baseRef`\n // restores the trusted instruction source for untrusted checkouts.\n try {\n const item = await options.storage.get({ orgId: binding.orgId, id: binding.workItemId });\n const foundSession = (await options.sessions?.getBySessionId(binding.sessionId)) ?? null;\n const sourceSession = foundSession?.orgId === binding.orgId ? foundSession : null;\n if (sourceSession) {\n updates.projectRepositoryId = sourceSession.projectRepositoryId;\n }\n const untrusted = updates.untrustedCheckout === true || item?.externalSource?.type === 'pull-request';\n if (untrusted) {\n updates.untrustedCheckout = true;\n const metadataBaseBranch = item?.metadata?.baseBranch;\n const baseRef =\n sourceSession?.baseBranch ||\n (typeof metadataBaseBranch === 'string' && metadataBaseBranch ? metadataBaseBranch : undefined);\n if (baseRef) updates.baseRef = baseRef;\n }\n } catch {\n // Enrichment is best-effort; the address itself only needs the binding.\n }\n try {\n await options.context.setState(updates);\n } catch {\n // State healing must never block tool registration on the recovered address.\n }\n}\n"],"mappings":";;AA0BA,SAAgB,6BACd,gBACwC;CACxC,IAAI,CAAC,kBAAkB,OAAO,eAAe,QAAQ,YAAY,OAAO;CACxE,MAAM,UAAU,eAAe,IAAI,YAAY;CAC/C,MAAM,mBAAmB,SAAS,SAAS,CAAC,CAAC;CAC7C,IAAI,CAAC,SAAS,YAAY,CAAC,QAAQ,cAAc,CAAC,kBAAkB,OAAO;CAC3E,OAAO;EACL;EACA,UAAU,QAAQ;EAClB,YAAY,QAAQ;EACpB,WAAW,QAAQ;CACrB;AACF;AAEA,SAAgB,yBAAyB,gBAA6E;CACpH,MAAM,cAAc,6BAA6B,cAAc;CAC/D,IAAI,CAAC,eAAe,CAAC,kBAAkB,OAAO,eAAe,QAAQ,YAAY,OAAO;CAExF,MAAM,QAAQ,oBADD,eAAe,IAAI,MACK,CAAC;CACtC,IAAI,CAAC,OAAO,OAAO;CACnB,OAAO;EAAE;EAAO,GAAG;CAAY;AACjC;;;;;;;;;;AAiBA,eAAsB,6BAA6B,SAIC;CAClD,MAAM,SAAS,yBAAyB,QAAQ,cAAc;CAC9D,IAAI,QAAQ,OAAO,EAAE,SAAS,OAAO;CAErC,MAAM,iBAAiB,QAAQ;CAC/B,IAAI,CAAC,kBAAkB,OAAO,eAAe,QAAQ,YAAY,OAAO;CACxE,MAAM,UAAU,eAAe,IAAI,YAAY;CAC/C,IAAI,CAAC,SAAS,YAAY,CAAC,QAAQ,YAAY,OAAO;CAEtD,MAAM,QAAQ,oBADD,eAAe,IAAI,MACK,CAAC;CACtC,IAAI,CAAC,OAAO,OAAO;CAEnB,MAAM,UAAU,MAAM,QAAQ,QAAQ,6BAA6B;EACjE;EACA,UAAU,QAAQ;EAClB,YAAY,QAAQ;EACpB,WAAW,QAAQ;CACrB,CAAC;CACD,IAAI,CAAC,SAAS,OAAO;CAErB,MAAM,0BAA0B;EAAE;EAAS;EAAS,SAAS,QAAQ;EAAS,UAAU,QAAQ;CAAS,CAAC;CAE1G,OAAO;EACL,SAAS;GACP;GACA,kBAAkB,QAAQ;GAC1B,UAAU,QAAQ;GAClB,YAAY,QAAQ;GACpB,WAAW,QAAQ;EACrB;EACA;CACF;AACF;AAEA,eAAe,0BAA0B,SAKvB;CAChB,MAAM,EAAE,YAAY;CACpB,MAAM,UAA+B,EAAE,kBAAkB,QAAQ,iBAAiB;CAIlF,IAAI,QAAQ,SAAS,UACnB,QAAQ,oBAAoB;CAK9B,IAAI;EACF,MAAM,OAAO,MAAM,QAAQ,QAAQ,IAAI;GAAE,OAAO,QAAQ;GAAO,IAAI,QAAQ;EAAW,CAAC;EACvF,MAAM,eAAgB,MAAM,QAAQ,UAAU,eAAe,QAAQ,SAAS,KAAM;EACpF,MAAM,gBAAgB,cAAc,UAAU,QAAQ,QAAQ,eAAe;EAC7E,IAAI,eACF,QAAQ,sBAAsB,cAAc;EAG9C,IADkB,QAAQ,sBAAsB,QAAQ,MAAM,gBAAgB,SAAS,gBACxE;GACb,QAAQ,oBAAoB;GAC5B,MAAM,qBAAqB,MAAM,UAAU;GAC3C,MAAM,UACJ,eAAe,eACd,OAAO,uBAAuB,YAAY,qBAAqB,qBAAqB,KAAA;GACvF,IAAI,SAAS,QAAQ,UAAU;EACjC;CACF,QAAQ,CAER;CACA,IAAI;EACF,MAAM,QAAQ,QAAQ,SAAS,OAAO;CACxC,QAAQ,CAER;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"defaults.d.ts","sourceRoot":"","sources":["../../src/rules/defaults.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EASV,YAAY,EACZ,qBAAqB,EAMtB,MAAM,YAAY,CAAC;AAGpB,eAAO,MAAM,4BAA4B,uBAAuB,CAAC;AAoVjE,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,qBAAqB,EAC3B,SAAS,GAAE,qBAA0B,GACpC,IAAI,CAAC,YAAY,EAAE,SAAS,CAAC,CAQ/B;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,qBAAqB,CAAA;CAAE,GAAG,YAAY,CAW/G;AAED,wBAAgB,mBAAmB,IAAI,YAAY,CAElD"}
1
+ {"version":3,"file":"defaults.d.ts","sourceRoot":"","sources":["../../src/rules/defaults.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EASV,YAAY,EACZ,qBAAqB,EAMtB,MAAM,YAAY,CAAC;AAGpB,eAAO,MAAM,4BAA4B,uBAAuB,CAAC;AAuajE,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,qBAAqB,EAC3B,SAAS,GAAE,qBAA0B,GACpC,IAAI,CAAC,YAAY,EAAE,SAAS,CAAC,CAQ/B;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,qBAAqB,CAAA;CAAE,GAAG,YAAY,CAW/G;AAED,wBAAgB,mBAAmB,IAAI,YAAY,CAElD"}
@@ -4,6 +4,9 @@ const DEFAULT_FACTORY_RULE_VERSION = "factory-default-v1";
4
4
  function trustedGithubActor(context) {
5
5
  return context.actor.type === "github" && context.actor.trusted;
6
6
  }
7
+ function githubActorLogin(context) {
8
+ return context.actor.type === "github" ? context.actor.login : void 0;
9
+ }
7
10
  function invokeIssueInvestigation(context) {
8
11
  return {
9
12
  type: "invokeSkill",
@@ -91,10 +94,26 @@ function issueOpened(context) {
91
94
  stage: trustedGithubActor(context) && createdAfterFactory(context.issue.createdAt, context.factory.createdAt) ? "triage" : "intake",
92
95
  metadata: {
93
96
  githubRepositoryId: context.repository.id,
94
- githubIssueNumber: context.issue.number
97
+ githubIssueNumber: context.issue.number,
98
+ ...githubActorLogin(context) ? { author: githubActorLogin(context) } : {},
99
+ assignees: context.issue.assignees ?? [],
100
+ labels: context.issue.labels ?? []
95
101
  }
96
102
  };
97
103
  }
104
+ function issueClosed(context) {
105
+ if (!context.item || context.item.source !== "github-issue" || !context.issue) return;
106
+ if (context.board !== "work") return;
107
+ if (context.item.stages.some((stage) => stage === "done" || stage === "canceled")) return;
108
+ const canceled = context.issue.stateReason === "not_planned" || context.issue.stateReason === "duplicate";
109
+ return {
110
+ type: "transition",
111
+ idempotencyKey: `${context.ingress.id}:issue-closed`,
112
+ board: "work",
113
+ stage: canceled ? "canceled" : "done",
114
+ message: { text: `GitHub issue #${context.issue.number} was closed${context.issue.stateReason ? ` (${context.issue.stateReason})` : ""}; this Work card was moved to ${canceled ? "Canceled" : "Done"}.` }
115
+ };
116
+ }
98
117
  function pullRequestOpened(context) {
99
118
  if (!context.pullRequest) return;
100
119
  return {
@@ -110,8 +129,15 @@ function pullRequestOpened(context) {
110
129
  githubRepositoryId: context.repository.id,
111
130
  githubPullRequestNumber: context.pullRequest.number,
112
131
  factoryAuthored: context.actor.type === "github" && context.actor.factoryAuthored,
132
+ state: context.pullRequest.state,
133
+ draft: context.pullRequest.draft,
134
+ merged: context.pullRequest.merged,
135
+ assignees: context.pullRequest.assignees ?? [],
136
+ requestedReviewers: context.pullRequest.requestedReviewers ?? [],
137
+ labels: context.pullRequest.labels ?? [],
113
138
  headBranch: context.pullRequest.headBranch,
114
- baseBranch: context.pullRequest.baseBranch
139
+ baseBranch: context.pullRequest.baseBranch,
140
+ ...githubActorLogin(context) ? { author: githubActorLogin(context) } : {}
115
141
  }
116
142
  };
117
143
  }
@@ -142,6 +168,19 @@ function pullRequestClosed(context) {
142
168
  message: { text: `Pull request #${context.pullRequest.number} was closed without merging; this Review card was moved to Canceled.` }
143
169
  };
144
170
  }
171
+ function reReviewRequestedPullRequest(context) {
172
+ if (!context.item || context.board !== "review" || !context.reviewRequest?.factoryReviewer) return;
173
+ if (!context.pullRequest || context.pullRequest.state !== "open" || context.pullRequest.merged) return;
174
+ if (!trustedGithubActor(context)) return;
175
+ if (context.actor.type === "github" && context.actor.factoryAuthored) return;
176
+ if (context.item.stages.length === 1 && context.item.stages[0] === "review") return;
177
+ return {
178
+ type: "transition",
179
+ idempotencyKey: `${context.ingress.id}:re-review-requested`,
180
+ board: "review",
181
+ stage: "review"
182
+ };
183
+ }
145
184
  function linearIssueObserved(context) {
146
185
  if (context.item) return;
147
186
  return {
@@ -155,15 +194,37 @@ function linearIssueObserved(context) {
155
194
  stage: "triage",
156
195
  metadata: {
157
196
  linearIssueId: context.issue.id,
158
- linearIssueIdentifier: context.issue.identifier,
197
+ identifier: context.issue.identifier,
159
198
  linearState: context.issue.state,
160
199
  linearStateType: context.issue.stateType,
161
200
  linearPriority: context.issue.priorityLabel,
162
201
  linearAssignee: context.issue.assignee,
163
- linearTeam: context.issue.team
202
+ linearCreator: context.issue.creator,
203
+ linearTeam: context.issue.team,
204
+ labels: [...context.issue.labels],
205
+ ...context.issue.assignee ? { assignee: context.issue.assignee } : {},
206
+ ...context.issue.creator ? {
207
+ creator: context.issue.creator,
208
+ author: context.issue.creator
209
+ } : {}
164
210
  }
165
211
  };
166
212
  }
213
+ function linearIssueClosed(context) {
214
+ if (!context.item || context.item.source !== "linear-issue") return;
215
+ if (context.board !== "work") return;
216
+ if (context.item.stages.some((stage) => stage === "done" || stage === "canceled")) return;
217
+ const stateType = context.issue.stateType;
218
+ if (stateType !== "completed" && stateType !== "canceled") return;
219
+ const canceled = stateType === "canceled";
220
+ return {
221
+ type: "transition",
222
+ idempotencyKey: `${context.ingress.id}:issue-closed`,
223
+ board: "work",
224
+ stage: canceled ? "canceled" : "done",
225
+ message: { text: `Linear issue ${context.issue.identifier} was ${canceled ? "canceled" : "completed"}; this Work card was moved to ${canceled ? "Canceled" : "Done"}.` }
226
+ };
227
+ }
167
228
  const BUILT_IN_DEFAULTS = {
168
229
  work: {
169
230
  triage: {
@@ -181,14 +242,19 @@ const BUILT_IN_DEFAULTS = {
181
242
  github: {
182
243
  issueOpened: { onEvent: issueOpened },
183
244
  issueEdited: { onEvent: retriageGithubIssue },
245
+ issueClosed: { onEvent: issueClosed },
184
246
  issueCommentCreated: { onEvent: retriageGithubIssue },
185
247
  issueCommentEdited: { onEvent: retriageGithubIssue },
186
248
  issueCommentDeleted: { onEvent: retriageGithubIssue },
187
249
  pullRequestOpened: { onEvent: pullRequestOpened },
250
+ pullRequestReviewRequested: { onEvent: reReviewRequestedPullRequest },
188
251
  pullRequestMerged: { onEvent: pullRequestMerged },
189
252
  pullRequestClosed: { onEvent: pullRequestClosed }
190
253
  },
191
- linear: { issueObserved: { onEvent: linearIssueObserved } }
254
+ linear: {
255
+ issueObserved: { onEvent: linearIssueObserved },
256
+ issueClosed: { onEvent: linearIssueClosed }
257
+ }
192
258
  };
193
259
  function mergeBoardRules(base, overrides) {
194
260
  const result = {};
@@ -1 +1 @@
1
- {"version":3,"file":"defaults.js","names":[],"sources":["../../src/rules/defaults.ts"],"sourcesContent":["import type {\n FactoryBoardRuleLeaf,\n FactoryBoardRules,\n FactoryGithubRuleLeaf,\n FactoryGithubEventName,\n FactoryGithubRuleContext,\n FactoryLinearEventName,\n FactoryLinearRuleContext,\n FactoryLinearRuleLeaf,\n FactoryRules,\n FactoryRulesOverrides,\n FactoryRuleSource,\n FactoryRuleStage,\n FactoryStageRuleContext,\n FactoryToolResultRuleContext,\n FactoryToolRuleLeaf,\n} from './types.js';\nimport { assertFactoryRules, FactoryRuleValidationError } from './validation.js';\n\nexport const DEFAULT_FACTORY_RULE_VERSION = 'factory-default-v1';\n\nfunction trustedGithubActor(context: Pick<FactoryStageRuleContext, 'actor'>): boolean {\n return context.actor.type === 'github' && context.actor.trusted;\n}\n\nfunction invokeIssueInvestigation(context: FactoryStageRuleContext) {\n return {\n type: 'invokeSkill',\n idempotencyKey: `${context.ingress.id}:factory-triage`,\n role: 'triage',\n skillName: 'factory-triage',\n arguments: context.item.url ? `GitHub issue (${context.item.url})` : context.item.title,\n } as const;\n}\n\nfunction investigateTriagedIssue(context: FactoryStageRuleContext) {\n if (context.cause === 'linked_item_materialized' && context.fromStage === 'intake' && context.toStage === 'triage') {\n return;\n }\n return invokeIssueInvestigation(context);\n}\n\nfunction retriageGithubIssue(context: FactoryGithubRuleContext) {\n if (!context.item || context.item.source !== 'github-issue' || !context.item.url) return;\n if (context.actor.type === 'github' && context.actor.factoryAuthored) return;\n\n const reason =\n context.event === 'issueEdited'\n ? context.issueChange?.title && context.issueChange.body\n ? 'issue title and body edited'\n : context.issueChange?.title\n ? 'issue title edited'\n : 'issue body edited'\n : context.event === 'issueCommentDeleted'\n ? 'comment deleted'\n : context.event === 'issueCommentEdited'\n ? 'comment edited'\n : 'comment created';\n return {\n type: 'invokeSkill',\n idempotencyKey: `${context.ingress.id}:factory-triage`,\n role: 'triage',\n skillName: 'factory-triage',\n arguments: `Re-triage GitHub issue (${context.item.url}) after ${reason}.`,\n } as const;\n}\n\nfunction investigateTriagedLinearIssue(context: FactoryStageRuleContext) {\n const identifier = context.item.sourceKey?.startsWith('linear:')\n ? context.item.sourceKey.slice('linear:'.length)\n : context.item.title;\n return {\n type: 'invokeSkill',\n idempotencyKey: `${context.ingress.id}:factory-triage-linear`,\n role: 'triage',\n skillName: 'factory-triage',\n arguments: `Linear issue ${identifier}${context.item.url ? ` (${context.item.url})` : ''}`,\n } as const;\n}\n\nfunction planWorkItem(context: FactoryStageRuleContext) {\n return {\n type: 'invokeSkill',\n idempotencyKey: `${context.ingress.id}:factory-plan`,\n role: 'plan',\n skillName: 'factory-plan',\n arguments: context.item.url ? `Work item (${context.item.url})` : context.item.title,\n } as const;\n}\n\nfunction reviewPullRequest(context: FactoryStageRuleContext) {\n return {\n type: 'invokeSkill',\n idempotencyKey: `${context.ingress.id}:factory-review`,\n role: 'review',\n skillName: 'factory-review',\n arguments: context.item.url ? `GitHub pull request (${context.item.url})` : context.item.title,\n } as const;\n}\n\nfunction resultContent(value: unknown): string | undefined {\n if (typeof value === 'string') return value;\n if (!value || typeof value !== 'object' || Array.isArray(value)) return;\n const content = (value as { content?: unknown }).content;\n return typeof content === 'string' ? content : undefined;\n}\n\n// Interactive-session path only: factory-plan never calls submit_plan — it\n// advances planning → execute via factory_transition_work_item directly.\nfunction advanceApprovedPlan(context: FactoryToolResultRuleContext) {\n if (\n context.result.status !== 'success' ||\n context.board !== 'work' ||\n context.item.stages.length !== 1 ||\n context.item.stages[0] !== 'planning' ||\n context.actor.type !== 'agent' ||\n context.actor.role !== 'plan' ||\n !resultContent(context.result.value)?.startsWith('Plan approved.')\n ) {\n return;\n }\n return {\n type: 'transition',\n idempotencyKey: `${context.ingress.id}:approved-plan`,\n board: 'work',\n stage: 'execute',\n } as const;\n}\n\nfunction createdAfterFactory(createdAt: string | undefined, factoryCreatedAt: string): boolean {\n if (!createdAt) return false;\n const sourceCreatedAt = Date.parse(createdAt);\n const projectCreatedAt = Date.parse(factoryCreatedAt);\n return Number.isFinite(sourceCreatedAt) && Number.isFinite(projectCreatedAt) && sourceCreatedAt > projectCreatedAt;\n}\n\nfunction issueOpened(context: FactoryGithubRuleContext) {\n if (!context.issue) return;\n return {\n type: 'upsertLinkedWorkItem',\n idempotencyKey: `${context.ingress.id}:issue-intake`,\n board: 'work',\n source: 'github-issue',\n sourceKey: `github-issue:${context.issue.number}`,\n title: context.issue.title,\n url: context.issue.url,\n stage:\n trustedGithubActor(context) && createdAfterFactory(context.issue.createdAt, context.factory.createdAt)\n ? 'triage'\n : 'intake',\n metadata: {\n githubRepositoryId: context.repository.id,\n githubIssueNumber: context.issue.number,\n },\n } as const;\n}\n\nfunction pullRequestOpened(context: FactoryGithubRuleContext) {\n if (!context.pullRequest) return;\n return {\n type: 'upsertLinkedWorkItem',\n idempotencyKey: `${context.ingress.id}:pull-request-intake`,\n board: 'review',\n source: 'github-pr',\n sourceKey: `github-pr:${context.pullRequest.number}`,\n title: context.pullRequest.title,\n url: context.pullRequest.url,\n stage:\n trustedGithubActor(context) && createdAfterFactory(context.pullRequest.createdAt, context.factory.createdAt)\n ? 'review'\n : 'intake',\n metadata: {\n githubRepositoryId: context.repository.id,\n githubPullRequestNumber: context.pullRequest.number,\n factoryAuthored: context.actor.type === 'github' && context.actor.factoryAuthored,\n headBranch: context.pullRequest.headBranch,\n baseBranch: context.pullRequest.baseBranch,\n },\n } as const;\n}\n\nfunction pullRequestMerged(context: FactoryGithubRuleContext) {\n if (!context.item || !context.pullRequest?.merged) return;\n if (context.board === 'review') {\n // The event is bound to the PR's own Review card: a merged PR is finished\n // review work, so always move the card to Done. The message only reaches\n // an active session (if any) — cards without one just move, instead of\n // failing retries against a binding that never existed.\n return {\n type: 'transition',\n idempotencyKey: `${context.ingress.id}:pull-request-merged`,\n board: 'review',\n stage: 'done',\n message: {\n text:\n `Pull request #${context.pullRequest.number} merged; this Review card was moved to Done. ` +\n 'No further review is needed unless follow-up work was requested.',\n },\n } as const;\n }\n // Provenance bound the event to the originating Work item instead: remind\n // its agent to assess completion — never auto-complete the Work item.\n return {\n type: 'sendMessage',\n idempotencyKey: `${context.ingress.id}:assess-work-completion`,\n role: 'work',\n message:\n `Pull request #${context.pullRequest.number} merged. Assess whether the linked Work item is complete. ` +\n 'Do not mark it Done solely because this PR merged; use factory_transition_work_item only after verifying the work.',\n } as const;\n}\n\nfunction pullRequestClosed(context: FactoryGithubRuleContext) {\n if (!context.item || !context.pullRequest || context.pullRequest.merged) return;\n if (context.board !== 'review') return;\n // A PR closed without merging is abandoned review work: clear the card off\n // the board instead of leaving it in Reviewing forever.\n return {\n type: 'transition',\n idempotencyKey: `${context.ingress.id}:pull-request-closed`,\n board: 'review',\n stage: 'canceled',\n message: {\n text:\n `Pull request #${context.pullRequest.number} was closed without merging; ` +\n 'this Review card was moved to Canceled.',\n },\n } as const;\n}\n\nfunction linearIssueObserved(context: FactoryLinearRuleContext) {\n if (context.item) return;\n return {\n type: 'upsertLinkedWorkItem',\n idempotencyKey: `${context.ingress.id}:issue-triage`,\n board: 'work',\n source: 'linear-issue',\n sourceKey: `linear:${context.issue.identifier}`,\n title: `${context.issue.identifier}: ${context.issue.title}`,\n url: context.issue.url,\n stage: 'triage',\n metadata: {\n linearIssueId: context.issue.id,\n linearIssueIdentifier: context.issue.identifier,\n linearState: context.issue.state,\n linearStateType: context.issue.stateType,\n linearPriority: context.issue.priorityLabel,\n linearAssignee: context.issue.assignee,\n linearTeam: context.issue.team,\n },\n } as const;\n}\n\nconst BUILT_IN_DEFAULTS: FactoryRulesOverrides = {\n work: {\n triage: {\n issue: { onEnter: investigateTriagedIssue },\n linearIssue: { onEnter: investigateTriagedLinearIssue },\n },\n planning: {\n issue: { onEnter: planWorkItem },\n linearIssue: { onEnter: planWorkItem },\n manual: { onEnter: planWorkItem },\n },\n },\n review: { review: { pullRequest: { onEnter: reviewPullRequest } } },\n tools: { submit_plan: { onResult: advanceApprovedPlan } },\n github: {\n issueOpened: { onEvent: issueOpened },\n issueEdited: { onEvent: retriageGithubIssue },\n issueCommentCreated: { onEvent: retriageGithubIssue },\n issueCommentEdited: { onEvent: retriageGithubIssue },\n issueCommentDeleted: { onEvent: retriageGithubIssue },\n pullRequestOpened: { onEvent: pullRequestOpened },\n pullRequestMerged: { onEvent: pullRequestMerged },\n pullRequestClosed: { onEvent: pullRequestClosed },\n },\n linear: { issueObserved: { onEvent: linearIssueObserved } },\n};\n\nfunction mergeBoardRules(\n base: FactoryBoardRules | undefined,\n overrides: FactoryBoardRules | undefined,\n): FactoryBoardRules {\n const result: FactoryBoardRules = {};\n const stages = new Set([...Object.keys(base ?? {}), ...Object.keys(overrides ?? {})]) as Set<FactoryRuleStage>;\n for (const stage of stages) {\n const baseSources = base?.[stage];\n const overrideSources = overrides?.[stage];\n const sources = new Set([\n ...Object.keys(baseSources ?? {}),\n ...Object.keys(overrideSources ?? {}),\n ]) as Set<FactoryRuleSource>;\n const mergedSources: Partial<Record<FactoryRuleSource, FactoryBoardRuleLeaf>> = {};\n for (const source of sources) {\n const baseLeaf = baseSources?.[source];\n const overrideLeaf = overrideSources?.[source];\n mergedSources[source] = {\n ...(baseLeaf?.onEnter ? { onEnter: baseLeaf.onEnter } : {}),\n ...(baseLeaf?.onExit ? { onExit: baseLeaf.onExit } : {}),\n ...(overrideLeaf && 'onEnter' in overrideLeaf ? { onEnter: overrideLeaf.onEnter } : {}),\n ...(overrideLeaf && 'onExit' in overrideLeaf ? { onExit: overrideLeaf.onExit } : {}),\n };\n }\n result[stage] = mergedSources;\n }\n return result;\n}\n\nfunction mergeToolRules(\n base: Record<string, FactoryToolRuleLeaf> | undefined,\n overrides: Record<string, FactoryToolRuleLeaf> | undefined,\n): Record<string, FactoryToolRuleLeaf> {\n const result: Record<string, FactoryToolRuleLeaf> = {};\n for (const name of new Set([...Object.keys(base ?? {}), ...Object.keys(overrides ?? {})])) {\n const baseLeaf = base?.[name];\n const overrideLeaf = overrides?.[name];\n result[name] = {\n ...(baseLeaf?.onResult ? { onResult: baseLeaf.onResult } : {}),\n ...(overrideLeaf && 'onResult' in overrideLeaf ? { onResult: overrideLeaf.onResult } : {}),\n };\n }\n return result;\n}\n\nfunction mergeGithubRules(\n base: FactoryRulesOverrides['github'],\n overrides: FactoryRulesOverrides['github'],\n): NonNullable<FactoryRulesOverrides['github']> {\n const result: Partial<Record<FactoryGithubEventName, FactoryGithubRuleLeaf>> = {};\n const events = new Set([...Object.keys(base ?? {}), ...Object.keys(overrides ?? {})]) as Set<FactoryGithubEventName>;\n for (const event of events) {\n const baseLeaf = base?.[event];\n const overrideLeaf = overrides?.[event];\n result[event] = {\n ...(baseLeaf?.onEvent ? { onEvent: baseLeaf.onEvent } : {}),\n ...(overrideLeaf && 'onEvent' in overrideLeaf ? { onEvent: overrideLeaf.onEvent } : {}),\n };\n }\n return result;\n}\n\nfunction mergeLinearRules(\n base: FactoryRulesOverrides['linear'],\n overrides: FactoryRulesOverrides['linear'],\n): NonNullable<FactoryRulesOverrides['linear']> {\n const result: Partial<Record<FactoryLinearEventName, FactoryLinearRuleLeaf>> = {};\n const events = new Set([...Object.keys(base ?? {}), ...Object.keys(overrides ?? {})]) as Set<FactoryLinearEventName>;\n for (const event of events) {\n const baseLeaf = base?.[event];\n const overrideLeaf = overrides?.[event];\n result[event] = {\n ...(baseLeaf?.onEvent ? { onEvent: baseLeaf.onEvent } : {}),\n ...(overrideLeaf && 'onEvent' in overrideLeaf ? { onEvent: overrideLeaf.onEvent } : {}),\n };\n }\n return result;\n}\n\nexport function mergeFactoryRuleOverrides(\n base: FactoryRulesOverrides,\n overrides: FactoryRulesOverrides = {},\n): Omit<FactoryRules, 'version'> {\n return {\n work: mergeBoardRules(base.work, overrides.work),\n review: mergeBoardRules(base.review, overrides.review),\n tools: mergeToolRules(base.tools, overrides.tools),\n github: mergeGithubRules(base.github, overrides.github),\n linear: mergeLinearRules(base.linear, overrides.linear),\n };\n}\n\nexport function defaultFactoryRules(input: { version: string; overrides?: FactoryRulesOverrides }): FactoryRules {\n if (typeof input?.version !== 'string' || input.version.trim().length === 0) {\n throw new FactoryRuleValidationError('Factory rule version is required.');\n }\n\n const rules: FactoryRules = {\n version: input.version.trim(),\n ...mergeFactoryRuleOverrides(BUILT_IN_DEFAULTS, input.overrides),\n };\n assertFactoryRules(rules);\n return rules;\n}\n\nexport function builtInFactoryRules(): FactoryRules {\n return defaultFactoryRules({ version: DEFAULT_FACTORY_RULE_VERSION });\n}\n"],"mappings":";;AAmBA,MAAa,+BAA+B;AAE5C,SAAS,mBAAmB,SAA0D;CACpF,OAAO,QAAQ,MAAM,SAAS,YAAY,QAAQ,MAAM;AAC1D;AAEA,SAAS,yBAAyB,SAAkC;CAClE,OAAO;EACL,MAAM;EACN,gBAAgB,GAAG,QAAQ,QAAQ,GAAG;EACtC,MAAM;EACN,WAAW;EACX,WAAW,QAAQ,KAAK,MAAM,iBAAiB,QAAQ,KAAK,IAAI,KAAK,QAAQ,KAAK;CACpF;AACF;AAEA,SAAS,wBAAwB,SAAkC;CACjE,IAAI,QAAQ,UAAU,8BAA8B,QAAQ,cAAc,YAAY,QAAQ,YAAY,UACxG;CAEF,OAAO,yBAAyB,OAAO;AACzC;AAEA,SAAS,oBAAoB,SAAmC;CAC9D,IAAI,CAAC,QAAQ,QAAQ,QAAQ,KAAK,WAAW,kBAAkB,CAAC,QAAQ,KAAK,KAAK;CAClF,IAAI,QAAQ,MAAM,SAAS,YAAY,QAAQ,MAAM,iBAAiB;CAEtE,MAAM,SACJ,QAAQ,UAAU,gBACd,QAAQ,aAAa,SAAS,QAAQ,YAAY,OAChD,gCACA,QAAQ,aAAa,QACnB,uBACA,sBACJ,QAAQ,UAAU,wBAChB,oBACA,QAAQ,UAAU,uBAChB,mBACA;CACV,OAAO;EACL,MAAM;EACN,gBAAgB,GAAG,QAAQ,QAAQ,GAAG;EACtC,MAAM;EACN,WAAW;EACX,WAAW,2BAA2B,QAAQ,KAAK,IAAI,UAAU,OAAO;CAC1E;AACF;AAEA,SAAS,8BAA8B,SAAkC;CACvE,MAAM,aAAa,QAAQ,KAAK,WAAW,WAAW,SAAS,IAC3D,QAAQ,KAAK,UAAU,MAAM,CAAgB,IAC7C,QAAQ,KAAK;CACjB,OAAO;EACL,MAAM;EACN,gBAAgB,GAAG,QAAQ,QAAQ,GAAG;EACtC,MAAM;EACN,WAAW;EACX,WAAW,gBAAgB,aAAa,QAAQ,KAAK,MAAM,KAAK,QAAQ,KAAK,IAAI,KAAK;CACxF;AACF;AAEA,SAAS,aAAa,SAAkC;CACtD,OAAO;EACL,MAAM;EACN,gBAAgB,GAAG,QAAQ,QAAQ,GAAG;EACtC,MAAM;EACN,WAAW;EACX,WAAW,QAAQ,KAAK,MAAM,cAAc,QAAQ,KAAK,IAAI,KAAK,QAAQ,KAAK;CACjF;AACF;AAEA,SAAS,kBAAkB,SAAkC;CAC3D,OAAO;EACL,MAAM;EACN,gBAAgB,GAAG,QAAQ,QAAQ,GAAG;EACtC,MAAM;EACN,WAAW;EACX,WAAW,QAAQ,KAAK,MAAM,wBAAwB,QAAQ,KAAK,IAAI,KAAK,QAAQ,KAAK;CAC3F;AACF;AAEA,SAAS,cAAc,OAAoC;CACzD,IAAI,OAAO,UAAU,UAAU,OAAO;CACtC,IAAI,CAAC,SAAS,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,GAAG;CACjE,MAAM,UAAW,MAAgC;CACjD,OAAO,OAAO,YAAY,WAAW,UAAU,KAAA;AACjD;AAIA,SAAS,oBAAoB,SAAuC;CAClE,IACE,QAAQ,OAAO,WAAW,aAC1B,QAAQ,UAAU,UAClB,QAAQ,KAAK,OAAO,WAAW,KAC/B,QAAQ,KAAK,OAAO,OAAO,cAC3B,QAAQ,MAAM,SAAS,WACvB,QAAQ,MAAM,SAAS,UACvB,CAAC,cAAc,QAAQ,OAAO,KAAK,CAAC,EAAE,WAAW,gBAAgB,GAEjE;CAEF,OAAO;EACL,MAAM;EACN,gBAAgB,GAAG,QAAQ,QAAQ,GAAG;EACtC,OAAO;EACP,OAAO;CACT;AACF;AAEA,SAAS,oBAAoB,WAA+B,kBAAmC;CAC7F,IAAI,CAAC,WAAW,OAAO;CACvB,MAAM,kBAAkB,KAAK,MAAM,SAAS;CAC5C,MAAM,mBAAmB,KAAK,MAAM,gBAAgB;CACpD,OAAO,OAAO,SAAS,eAAe,KAAK,OAAO,SAAS,gBAAgB,KAAK,kBAAkB;AACpG;AAEA,SAAS,YAAY,SAAmC;CACtD,IAAI,CAAC,QAAQ,OAAO;CACpB,OAAO;EACL,MAAM;EACN,gBAAgB,GAAG,QAAQ,QAAQ,GAAG;EACtC,OAAO;EACP,QAAQ;EACR,WAAW,gBAAgB,QAAQ,MAAM;EACzC,OAAO,QAAQ,MAAM;EACrB,KAAK,QAAQ,MAAM;EACnB,OACE,mBAAmB,OAAO,KAAK,oBAAoB,QAAQ,MAAM,WAAW,QAAQ,QAAQ,SAAS,IACjG,WACA;EACN,UAAU;GACR,oBAAoB,QAAQ,WAAW;GACvC,mBAAmB,QAAQ,MAAM;EACnC;CACF;AACF;AAEA,SAAS,kBAAkB,SAAmC;CAC5D,IAAI,CAAC,QAAQ,aAAa;CAC1B,OAAO;EACL,MAAM;EACN,gBAAgB,GAAG,QAAQ,QAAQ,GAAG;EACtC,OAAO;EACP,QAAQ;EACR,WAAW,aAAa,QAAQ,YAAY;EAC5C,OAAO,QAAQ,YAAY;EAC3B,KAAK,QAAQ,YAAY;EACzB,OACE,mBAAmB,OAAO,KAAK,oBAAoB,QAAQ,YAAY,WAAW,QAAQ,QAAQ,SAAS,IACvG,WACA;EACN,UAAU;GACR,oBAAoB,QAAQ,WAAW;GACvC,yBAAyB,QAAQ,YAAY;GAC7C,iBAAiB,QAAQ,MAAM,SAAS,YAAY,QAAQ,MAAM;GAClE,YAAY,QAAQ,YAAY;GAChC,YAAY,QAAQ,YAAY;EAClC;CACF;AACF;AAEA,SAAS,kBAAkB,SAAmC;CAC5D,IAAI,CAAC,QAAQ,QAAQ,CAAC,QAAQ,aAAa,QAAQ;CACnD,IAAI,QAAQ,UAAU,UAKpB,OAAO;EACL,MAAM;EACN,gBAAgB,GAAG,QAAQ,QAAQ,GAAG;EACtC,OAAO;EACP,OAAO;EACP,SAAS,EACP,MACE,iBAAiB,QAAQ,YAAY,OAAO,+GAEhD;CACF;CAIF,OAAO;EACL,MAAM;EACN,gBAAgB,GAAG,QAAQ,QAAQ,GAAG;EACtC,MAAM;EACN,SACE,iBAAiB,QAAQ,YAAY,OAAO;CAEhD;AACF;AAEA,SAAS,kBAAkB,SAAmC;CAC5D,IAAI,CAAC,QAAQ,QAAQ,CAAC,QAAQ,eAAe,QAAQ,YAAY,QAAQ;CACzE,IAAI,QAAQ,UAAU,UAAU;CAGhC,OAAO;EACL,MAAM;EACN,gBAAgB,GAAG,QAAQ,QAAQ,GAAG;EACtC,OAAO;EACP,OAAO;EACP,SAAS,EACP,MACE,iBAAiB,QAAQ,YAAY,OAAO,sEAEhD;CACF;AACF;AAEA,SAAS,oBAAoB,SAAmC;CAC9D,IAAI,QAAQ,MAAM;CAClB,OAAO;EACL,MAAM;EACN,gBAAgB,GAAG,QAAQ,QAAQ,GAAG;EACtC,OAAO;EACP,QAAQ;EACR,WAAW,UAAU,QAAQ,MAAM;EACnC,OAAO,GAAG,QAAQ,MAAM,WAAW,IAAI,QAAQ,MAAM;EACrD,KAAK,QAAQ,MAAM;EACnB,OAAO;EACP,UAAU;GACR,eAAe,QAAQ,MAAM;GAC7B,uBAAuB,QAAQ,MAAM;GACrC,aAAa,QAAQ,MAAM;GAC3B,iBAAiB,QAAQ,MAAM;GAC/B,gBAAgB,QAAQ,MAAM;GAC9B,gBAAgB,QAAQ,MAAM;GAC9B,YAAY,QAAQ,MAAM;EAC5B;CACF;AACF;AAEA,MAAM,oBAA2C;CAC/C,MAAM;EACJ,QAAQ;GACN,OAAO,EAAE,SAAS,wBAAwB;GAC1C,aAAa,EAAE,SAAS,8BAA8B;EACxD;EACA,UAAU;GACR,OAAO,EAAE,SAAS,aAAa;GAC/B,aAAa,EAAE,SAAS,aAAa;GACrC,QAAQ,EAAE,SAAS,aAAa;EAClC;CACF;CACA,QAAQ,EAAE,QAAQ,EAAE,aAAa,EAAE,SAAS,kBAAkB,EAAE,EAAE;CAClE,OAAO,EAAE,aAAa,EAAE,UAAU,oBAAoB,EAAE;CACxD,QAAQ;EACN,aAAa,EAAE,SAAS,YAAY;EACpC,aAAa,EAAE,SAAS,oBAAoB;EAC5C,qBAAqB,EAAE,SAAS,oBAAoB;EACpD,oBAAoB,EAAE,SAAS,oBAAoB;EACnD,qBAAqB,EAAE,SAAS,oBAAoB;EACpD,mBAAmB,EAAE,SAAS,kBAAkB;EAChD,mBAAmB,EAAE,SAAS,kBAAkB;EAChD,mBAAmB,EAAE,SAAS,kBAAkB;CAClD;CACA,QAAQ,EAAE,eAAe,EAAE,SAAS,oBAAoB,EAAE;AAC5D;AAEA,SAAS,gBACP,MACA,WACmB;CACnB,MAAM,SAA4B,CAAC;CACnC,MAAM,yBAAS,IAAI,IAAI,CAAC,GAAG,OAAO,KAAK,QAAQ,CAAC,CAAC,GAAG,GAAG,OAAO,KAAK,aAAa,CAAC,CAAC,CAAC,CAAC;CACpF,KAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,cAAc,OAAO;EAC3B,MAAM,kBAAkB,YAAY;EACpC,MAAM,0BAAU,IAAI,IAAI,CACtB,GAAG,OAAO,KAAK,eAAe,CAAC,CAAC,GAChC,GAAG,OAAO,KAAK,mBAAmB,CAAC,CAAC,CACtC,CAAC;EACD,MAAM,gBAA0E,CAAC;EACjF,KAAK,MAAM,UAAU,SAAS;GAC5B,MAAM,WAAW,cAAc;GAC/B,MAAM,eAAe,kBAAkB;GACvC,cAAc,UAAU;IACtB,GAAI,UAAU,UAAU,EAAE,SAAS,SAAS,QAAQ,IAAI,CAAC;IACzD,GAAI,UAAU,SAAS,EAAE,QAAQ,SAAS,OAAO,IAAI,CAAC;IACtD,GAAI,gBAAgB,aAAa,eAAe,EAAE,SAAS,aAAa,QAAQ,IAAI,CAAC;IACrF,GAAI,gBAAgB,YAAY,eAAe,EAAE,QAAQ,aAAa,OAAO,IAAI,CAAC;GACpF;EACF;EACA,OAAO,SAAS;CAClB;CACA,OAAO;AACT;AAEA,SAAS,eACP,MACA,WACqC;CACrC,MAAM,SAA8C,CAAC;CACrD,KAAK,MAAM,wBAAQ,IAAI,IAAI,CAAC,GAAG,OAAO,KAAK,QAAQ,CAAC,CAAC,GAAG,GAAG,OAAO,KAAK,aAAa,CAAC,CAAC,CAAC,CAAC,GAAG;EACzF,MAAM,WAAW,OAAO;EACxB,MAAM,eAAe,YAAY;EACjC,OAAO,QAAQ;GACb,GAAI,UAAU,WAAW,EAAE,UAAU,SAAS,SAAS,IAAI,CAAC;GAC5D,GAAI,gBAAgB,cAAc,eAAe,EAAE,UAAU,aAAa,SAAS,IAAI,CAAC;EAC1F;CACF;CACA,OAAO;AACT;AAEA,SAAS,iBACP,MACA,WAC8C;CAC9C,MAAM,SAAyE,CAAC;CAChF,MAAM,yBAAS,IAAI,IAAI,CAAC,GAAG,OAAO,KAAK,QAAQ,CAAC,CAAC,GAAG,GAAG,OAAO,KAAK,aAAa,CAAC,CAAC,CAAC,CAAC;CACpF,KAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,WAAW,OAAO;EACxB,MAAM,eAAe,YAAY;EACjC,OAAO,SAAS;GACd,GAAI,UAAU,UAAU,EAAE,SAAS,SAAS,QAAQ,IAAI,CAAC;GACzD,GAAI,gBAAgB,aAAa,eAAe,EAAE,SAAS,aAAa,QAAQ,IAAI,CAAC;EACvF;CACF;CACA,OAAO;AACT;AAEA,SAAS,iBACP,MACA,WAC8C;CAC9C,MAAM,SAAyE,CAAC;CAChF,MAAM,yBAAS,IAAI,IAAI,CAAC,GAAG,OAAO,KAAK,QAAQ,CAAC,CAAC,GAAG,GAAG,OAAO,KAAK,aAAa,CAAC,CAAC,CAAC,CAAC;CACpF,KAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,WAAW,OAAO;EACxB,MAAM,eAAe,YAAY;EACjC,OAAO,SAAS;GACd,GAAI,UAAU,UAAU,EAAE,SAAS,SAAS,QAAQ,IAAI,CAAC;GACzD,GAAI,gBAAgB,aAAa,eAAe,EAAE,SAAS,aAAa,QAAQ,IAAI,CAAC;EACvF;CACF;CACA,OAAO;AACT;AAEA,SAAgB,0BACd,MACA,YAAmC,CAAC,GACL;CAC/B,OAAO;EACL,MAAM,gBAAgB,KAAK,MAAM,UAAU,IAAI;EAC/C,QAAQ,gBAAgB,KAAK,QAAQ,UAAU,MAAM;EACrD,OAAO,eAAe,KAAK,OAAO,UAAU,KAAK;EACjD,QAAQ,iBAAiB,KAAK,QAAQ,UAAU,MAAM;EACtD,QAAQ,iBAAiB,KAAK,QAAQ,UAAU,MAAM;CACxD;AACF;AAEA,SAAgB,oBAAoB,OAA6E;CAC/G,IAAI,OAAO,OAAO,YAAY,YAAY,MAAM,QAAQ,KAAK,CAAC,CAAC,WAAW,GACxE,MAAM,IAAI,2BAA2B,mCAAmC;CAG1E,MAAM,QAAsB;EAC1B,SAAS,MAAM,QAAQ,KAAK;EAC5B,GAAG,0BAA0B,mBAAmB,MAAM,SAAS;CACjE;CACA,mBAAmB,KAAK;CACxB,OAAO;AACT;AAEA,SAAgB,sBAAoC;CAClD,OAAO,oBAAoB,EAAE,SAAS,6BAA6B,CAAC;AACtE"}
1
+ {"version":3,"file":"defaults.js","names":[],"sources":["../../src/rules/defaults.ts"],"sourcesContent":["import type {\n FactoryBoardRuleLeaf,\n FactoryBoardRules,\n FactoryGithubRuleLeaf,\n FactoryGithubEventName,\n FactoryGithubRuleContext,\n FactoryLinearEventName,\n FactoryLinearRuleContext,\n FactoryLinearRuleLeaf,\n FactoryRules,\n FactoryRulesOverrides,\n FactoryRuleSource,\n FactoryRuleStage,\n FactoryStageRuleContext,\n FactoryToolResultRuleContext,\n FactoryToolRuleLeaf,\n} from './types.js';\nimport { assertFactoryRules, FactoryRuleValidationError } from './validation.js';\n\nexport const DEFAULT_FACTORY_RULE_VERSION = 'factory-default-v1';\n\nfunction trustedGithubActor(context: Pick<FactoryStageRuleContext, 'actor'>): boolean {\n return context.actor.type === 'github' && context.actor.trusted;\n}\n\nfunction githubActorLogin(context: Pick<FactoryStageRuleContext, 'actor'>): string | undefined {\n return context.actor.type === 'github' ? context.actor.login : undefined;\n}\n\nfunction invokeIssueInvestigation(context: FactoryStageRuleContext) {\n return {\n type: 'invokeSkill',\n idempotencyKey: `${context.ingress.id}:factory-triage`,\n role: 'triage',\n skillName: 'factory-triage',\n arguments: context.item.url ? `GitHub issue (${context.item.url})` : context.item.title,\n } as const;\n}\n\nfunction investigateTriagedIssue(context: FactoryStageRuleContext) {\n if (context.cause === 'linked_item_materialized' && context.fromStage === 'intake' && context.toStage === 'triage') {\n return;\n }\n return invokeIssueInvestigation(context);\n}\n\nfunction retriageGithubIssue(context: FactoryGithubRuleContext) {\n if (!context.item || context.item.source !== 'github-issue' || !context.item.url) return;\n if (context.actor.type === 'github' && context.actor.factoryAuthored) return;\n\n const reason =\n context.event === 'issueEdited'\n ? context.issueChange?.title && context.issueChange.body\n ? 'issue title and body edited'\n : context.issueChange?.title\n ? 'issue title edited'\n : 'issue body edited'\n : context.event === 'issueCommentDeleted'\n ? 'comment deleted'\n : context.event === 'issueCommentEdited'\n ? 'comment edited'\n : 'comment created';\n return {\n type: 'invokeSkill',\n idempotencyKey: `${context.ingress.id}:factory-triage`,\n role: 'triage',\n skillName: 'factory-triage',\n arguments: `Re-triage GitHub issue (${context.item.url}) after ${reason}.`,\n } as const;\n}\n\nfunction investigateTriagedLinearIssue(context: FactoryStageRuleContext) {\n const identifier = context.item.sourceKey?.startsWith('linear:')\n ? context.item.sourceKey.slice('linear:'.length)\n : context.item.title;\n return {\n type: 'invokeSkill',\n idempotencyKey: `${context.ingress.id}:factory-triage-linear`,\n role: 'triage',\n skillName: 'factory-triage',\n arguments: `Linear issue ${identifier}${context.item.url ? ` (${context.item.url})` : ''}`,\n } as const;\n}\n\nfunction planWorkItem(context: FactoryStageRuleContext) {\n return {\n type: 'invokeSkill',\n idempotencyKey: `${context.ingress.id}:factory-plan`,\n role: 'plan',\n skillName: 'factory-plan',\n arguments: context.item.url ? `Work item (${context.item.url})` : context.item.title,\n } as const;\n}\n\nfunction reviewPullRequest(context: FactoryStageRuleContext) {\n return {\n type: 'invokeSkill',\n idempotencyKey: `${context.ingress.id}:factory-review`,\n role: 'review',\n skillName: 'factory-review',\n arguments: context.item.url ? `GitHub pull request (${context.item.url})` : context.item.title,\n } as const;\n}\n\nfunction resultContent(value: unknown): string | undefined {\n if (typeof value === 'string') return value;\n if (!value || typeof value !== 'object' || Array.isArray(value)) return;\n const content = (value as { content?: unknown }).content;\n return typeof content === 'string' ? content : undefined;\n}\n\n// Interactive-session path only: factory-plan never calls submit_plan — it\n// advances planning → execute via factory_transition_work_item directly.\nfunction advanceApprovedPlan(context: FactoryToolResultRuleContext) {\n if (\n context.result.status !== 'success' ||\n context.board !== 'work' ||\n context.item.stages.length !== 1 ||\n context.item.stages[0] !== 'planning' ||\n context.actor.type !== 'agent' ||\n context.actor.role !== 'plan' ||\n !resultContent(context.result.value)?.startsWith('Plan approved.')\n ) {\n return;\n }\n return {\n type: 'transition',\n idempotencyKey: `${context.ingress.id}:approved-plan`,\n board: 'work',\n stage: 'execute',\n } as const;\n}\n\nfunction createdAfterFactory(createdAt: string | undefined, factoryCreatedAt: string): boolean {\n if (!createdAt) return false;\n const sourceCreatedAt = Date.parse(createdAt);\n const projectCreatedAt = Date.parse(factoryCreatedAt);\n return Number.isFinite(sourceCreatedAt) && Number.isFinite(projectCreatedAt) && sourceCreatedAt > projectCreatedAt;\n}\n\nfunction issueOpened(context: FactoryGithubRuleContext) {\n if (!context.issue) return;\n return {\n type: 'upsertLinkedWorkItem',\n idempotencyKey: `${context.ingress.id}:issue-intake`,\n board: 'work',\n source: 'github-issue',\n sourceKey: `github-issue:${context.issue.number}`,\n title: context.issue.title,\n url: context.issue.url,\n stage:\n trustedGithubActor(context) && createdAfterFactory(context.issue.createdAt, context.factory.createdAt)\n ? 'triage'\n : 'intake',\n metadata: {\n githubRepositoryId: context.repository.id,\n githubIssueNumber: context.issue.number,\n ...(githubActorLogin(context) ? { author: githubActorLogin(context) } : {}),\n assignees: context.issue.assignees ?? [],\n labels: context.issue.labels ?? [],\n },\n } as const;\n}\n\nfunction issueClosed(context: FactoryGithubRuleContext) {\n if (!context.item || context.item.source !== 'github-issue' || !context.issue) return;\n if (context.board !== 'work') return;\n // Already off the board: nothing to reconcile.\n if (context.item.stages.some(stage => stage === 'done' || stage === 'canceled')) return;\n // Issue closure is a repository fact, not third-party input — no actor trust\n // gate. `not_planned` (and `duplicate`) means abandoned, everything else is\n // completed work.\n const canceled = context.issue.stateReason === 'not_planned' || context.issue.stateReason === 'duplicate';\n return {\n type: 'transition',\n idempotencyKey: `${context.ingress.id}:issue-closed`,\n board: 'work',\n stage: canceled ? 'canceled' : 'done',\n message: {\n text:\n `GitHub issue #${context.issue.number} was closed` +\n `${context.issue.stateReason ? ` (${context.issue.stateReason})` : ''}; ` +\n `this Work card was moved to ${canceled ? 'Canceled' : 'Done'}.`,\n },\n } as const;\n}\n\nfunction pullRequestOpened(context: FactoryGithubRuleContext) {\n if (!context.pullRequest) return;\n return {\n type: 'upsertLinkedWorkItem',\n idempotencyKey: `${context.ingress.id}:pull-request-intake`,\n board: 'review',\n source: 'github-pr',\n sourceKey: `github-pr:${context.pullRequest.number}`,\n title: context.pullRequest.title,\n url: context.pullRequest.url,\n stage:\n trustedGithubActor(context) && createdAfterFactory(context.pullRequest.createdAt, context.factory.createdAt)\n ? 'review'\n : 'intake',\n metadata: {\n githubRepositoryId: context.repository.id,\n githubPullRequestNumber: context.pullRequest.number,\n factoryAuthored: context.actor.type === 'github' && context.actor.factoryAuthored,\n state: context.pullRequest.state,\n draft: context.pullRequest.draft,\n merged: context.pullRequest.merged,\n assignees: context.pullRequest.assignees ?? [],\n requestedReviewers: context.pullRequest.requestedReviewers ?? [],\n labels: context.pullRequest.labels ?? [],\n headBranch: context.pullRequest.headBranch,\n baseBranch: context.pullRequest.baseBranch,\n ...(githubActorLogin(context) ? { author: githubActorLogin(context) } : {}),\n },\n } as const;\n}\n\nfunction pullRequestMerged(context: FactoryGithubRuleContext) {\n if (!context.item || !context.pullRequest?.merged) return;\n if (context.board === 'review') {\n // The event is bound to the PR's own Review card: a merged PR is finished\n // review work, so always move the card to Done. The message only reaches\n // an active session (if any) — cards without one just move, instead of\n // failing retries against a binding that never existed.\n return {\n type: 'transition',\n idempotencyKey: `${context.ingress.id}:pull-request-merged`,\n board: 'review',\n stage: 'done',\n message: {\n text:\n `Pull request #${context.pullRequest.number} merged; this Review card was moved to Done. ` +\n 'No further review is needed unless follow-up work was requested.',\n },\n } as const;\n }\n // Provenance bound the event to the originating Work item instead: remind\n // its agent to assess completion — never auto-complete the Work item.\n return {\n type: 'sendMessage',\n idempotencyKey: `${context.ingress.id}:assess-work-completion`,\n role: 'work',\n message:\n `Pull request #${context.pullRequest.number} merged. Assess whether the linked Work item is complete. ` +\n 'Do not mark it Done solely because this PR merged; use factory_transition_work_item only after verifying the work.',\n } as const;\n}\n\nfunction pullRequestClosed(context: FactoryGithubRuleContext) {\n if (!context.item || !context.pullRequest || context.pullRequest.merged) return;\n if (context.board !== 'review') return;\n // A PR closed without merging is abandoned review work: clear the card off\n // the board instead of leaving it in Reviewing forever.\n return {\n type: 'transition',\n idempotencyKey: `${context.ingress.id}:pull-request-closed`,\n board: 'review',\n stage: 'canceled',\n message: {\n text:\n `Pull request #${context.pullRequest.number} was closed without merging; ` +\n 'this Review card was moved to Canceled.',\n },\n } as const;\n}\n\nfunction reReviewRequestedPullRequest(context: FactoryGithubRuleContext) {\n // Only a review re-requested *from Factory's own bot* restarts the review —\n // requesting a human reviewer is not Factory's signal.\n if (!context.item || context.board !== 'review' || !context.reviewRequest?.factoryReviewer) return;\n if (!context.pullRequest || context.pullRequest.state !== 'open' || context.pullRequest.merged) return;\n // Trusted (write/admin) requesters only: re-entering review checks out and\n // executes PR code, the same bar pullRequestOpened applies to auto-review.\n if (!trustedGithubActor(context)) return;\n if (context.actor.type === 'github' && context.actor.factoryAuthored) return;\n // Already in Reviewing: a review pass is pending or running; re-entering\n // would be a same-stage no-op anyway (stage rules only fire on change).\n if (context.item.stages.length === 1 && context.item.stages[0] === 'review') return;\n return {\n type: 'transition',\n idempotencyKey: `${context.ingress.id}:re-review-requested`,\n board: 'review',\n stage: 'review',\n } as const;\n}\n\nfunction linearIssueObserved(context: FactoryLinearRuleContext) {\n if (context.item) return;\n return {\n type: 'upsertLinkedWorkItem',\n idempotencyKey: `${context.ingress.id}:issue-triage`,\n board: 'work',\n source: 'linear-issue',\n sourceKey: `linear:${context.issue.identifier}`,\n title: `${context.issue.identifier}: ${context.issue.title}`,\n url: context.issue.url,\n stage: 'triage',\n metadata: {\n linearIssueId: context.issue.id,\n identifier: context.issue.identifier,\n linearState: context.issue.state,\n linearStateType: context.issue.stateType,\n linearPriority: context.issue.priorityLabel,\n linearAssignee: context.issue.assignee,\n linearCreator: context.issue.creator,\n linearTeam: context.issue.team,\n labels: [...context.issue.labels] as string[],\n ...(context.issue.assignee ? { assignee: context.issue.assignee } : {}),\n ...(context.issue.creator ? { creator: context.issue.creator, author: context.issue.creator } : {}),\n },\n } as const;\n}\n\nfunction linearIssueClosed(context: FactoryLinearRuleContext) {\n if (!context.item || context.item.source !== 'linear-issue') return;\n if (context.board !== 'work') return;\n // Already off the board: nothing to reconcile.\n if (context.item.stages.some(stage => stage === 'done' || stage === 'canceled')) return;\n // Only terminal state types trigger close.\n const stateType = context.issue.stateType;\n if (stateType !== 'completed' && stateType !== 'canceled') return;\n const canceled = stateType === 'canceled';\n return {\n type: 'transition',\n idempotencyKey: `${context.ingress.id}:issue-closed`,\n board: 'work',\n stage: canceled ? 'canceled' : 'done',\n message: {\n text: `Linear issue ${context.issue.identifier} was ${canceled ? 'canceled' : 'completed'}; this Work card was moved to ${canceled ? 'Canceled' : 'Done'}.`,\n },\n } as const;\n}\n\nconst BUILT_IN_DEFAULTS: FactoryRulesOverrides = {\n work: {\n triage: {\n issue: { onEnter: investigateTriagedIssue },\n linearIssue: { onEnter: investigateTriagedLinearIssue },\n },\n planning: {\n issue: { onEnter: planWorkItem },\n linearIssue: { onEnter: planWorkItem },\n manual: { onEnter: planWorkItem },\n },\n },\n review: { review: { pullRequest: { onEnter: reviewPullRequest } } },\n tools: { submit_plan: { onResult: advanceApprovedPlan } },\n github: {\n issueOpened: { onEvent: issueOpened },\n issueEdited: { onEvent: retriageGithubIssue },\n issueClosed: { onEvent: issueClosed },\n issueCommentCreated: { onEvent: retriageGithubIssue },\n issueCommentEdited: { onEvent: retriageGithubIssue },\n issueCommentDeleted: { onEvent: retriageGithubIssue },\n pullRequestOpened: { onEvent: pullRequestOpened },\n pullRequestReviewRequested: { onEvent: reReviewRequestedPullRequest },\n pullRequestMerged: { onEvent: pullRequestMerged },\n pullRequestClosed: { onEvent: pullRequestClosed },\n },\n linear: { issueObserved: { onEvent: linearIssueObserved }, issueClosed: { onEvent: linearIssueClosed } },\n};\n\nfunction mergeBoardRules(\n base: FactoryBoardRules | undefined,\n overrides: FactoryBoardRules | undefined,\n): FactoryBoardRules {\n const result: FactoryBoardRules = {};\n const stages = new Set([...Object.keys(base ?? {}), ...Object.keys(overrides ?? {})]) as Set<FactoryRuleStage>;\n for (const stage of stages) {\n const baseSources = base?.[stage];\n const overrideSources = overrides?.[stage];\n const sources = new Set([\n ...Object.keys(baseSources ?? {}),\n ...Object.keys(overrideSources ?? {}),\n ]) as Set<FactoryRuleSource>;\n const mergedSources: Partial<Record<FactoryRuleSource, FactoryBoardRuleLeaf>> = {};\n for (const source of sources) {\n const baseLeaf = baseSources?.[source];\n const overrideLeaf = overrideSources?.[source];\n mergedSources[source] = {\n ...(baseLeaf?.onEnter ? { onEnter: baseLeaf.onEnter } : {}),\n ...(baseLeaf?.onExit ? { onExit: baseLeaf.onExit } : {}),\n ...(overrideLeaf && 'onEnter' in overrideLeaf ? { onEnter: overrideLeaf.onEnter } : {}),\n ...(overrideLeaf && 'onExit' in overrideLeaf ? { onExit: overrideLeaf.onExit } : {}),\n };\n }\n result[stage] = mergedSources;\n }\n return result;\n}\n\nfunction mergeToolRules(\n base: Record<string, FactoryToolRuleLeaf> | undefined,\n overrides: Record<string, FactoryToolRuleLeaf> | undefined,\n): Record<string, FactoryToolRuleLeaf> {\n const result: Record<string, FactoryToolRuleLeaf> = {};\n for (const name of new Set([...Object.keys(base ?? {}), ...Object.keys(overrides ?? {})])) {\n const baseLeaf = base?.[name];\n const overrideLeaf = overrides?.[name];\n result[name] = {\n ...(baseLeaf?.onResult ? { onResult: baseLeaf.onResult } : {}),\n ...(overrideLeaf && 'onResult' in overrideLeaf ? { onResult: overrideLeaf.onResult } : {}),\n };\n }\n return result;\n}\n\nfunction mergeGithubRules(\n base: FactoryRulesOverrides['github'],\n overrides: FactoryRulesOverrides['github'],\n): NonNullable<FactoryRulesOverrides['github']> {\n const result: Partial<Record<FactoryGithubEventName, FactoryGithubRuleLeaf>> = {};\n const events = new Set([...Object.keys(base ?? {}), ...Object.keys(overrides ?? {})]) as Set<FactoryGithubEventName>;\n for (const event of events) {\n const baseLeaf = base?.[event];\n const overrideLeaf = overrides?.[event];\n result[event] = {\n ...(baseLeaf?.onEvent ? { onEvent: baseLeaf.onEvent } : {}),\n ...(overrideLeaf && 'onEvent' in overrideLeaf ? { onEvent: overrideLeaf.onEvent } : {}),\n };\n }\n return result;\n}\n\nfunction mergeLinearRules(\n base: FactoryRulesOverrides['linear'],\n overrides: FactoryRulesOverrides['linear'],\n): NonNullable<FactoryRulesOverrides['linear']> {\n const result: Partial<Record<FactoryLinearEventName, FactoryLinearRuleLeaf>> = {};\n const events = new Set([...Object.keys(base ?? {}), ...Object.keys(overrides ?? {})]) as Set<FactoryLinearEventName>;\n for (const event of events) {\n const baseLeaf = base?.[event];\n const overrideLeaf = overrides?.[event];\n result[event] = {\n ...(baseLeaf?.onEvent ? { onEvent: baseLeaf.onEvent } : {}),\n ...(overrideLeaf && 'onEvent' in overrideLeaf ? { onEvent: overrideLeaf.onEvent } : {}),\n };\n }\n return result;\n}\n\nexport function mergeFactoryRuleOverrides(\n base: FactoryRulesOverrides,\n overrides: FactoryRulesOverrides = {},\n): Omit<FactoryRules, 'version'> {\n return {\n work: mergeBoardRules(base.work, overrides.work),\n review: mergeBoardRules(base.review, overrides.review),\n tools: mergeToolRules(base.tools, overrides.tools),\n github: mergeGithubRules(base.github, overrides.github),\n linear: mergeLinearRules(base.linear, overrides.linear),\n };\n}\n\nexport function defaultFactoryRules(input: { version: string; overrides?: FactoryRulesOverrides }): FactoryRules {\n if (typeof input?.version !== 'string' || input.version.trim().length === 0) {\n throw new FactoryRuleValidationError('Factory rule version is required.');\n }\n\n const rules: FactoryRules = {\n version: input.version.trim(),\n ...mergeFactoryRuleOverrides(BUILT_IN_DEFAULTS, input.overrides),\n };\n assertFactoryRules(rules);\n return rules;\n}\n\nexport function builtInFactoryRules(): FactoryRules {\n return defaultFactoryRules({ version: DEFAULT_FACTORY_RULE_VERSION });\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"start-coordinator.d.ts","sourceRoot":"","sources":["../../src/rules/start-coordinator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAG9D,OAAO,KAAK,EAAwB,qBAAqB,EAAE,MAAM,4CAA4C,CAAC;AAC9G,OAAO,KAAK,EAAwB,0BAA0B,EAAE,MAAM,2CAA2C,CAAC;AAClH,OAAO,KAAK,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AACnG,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AACxE,OAAO,KAAK,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAE5E,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,EAAE,MAAM,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE;QAAE,IAAI,EAAE,QAAQ,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,IAAI,EAAE,OAAO,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1G,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE;QACR,EAAE,CAAC,EAAE,MAAM,CAAC;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,mBAAmB,CAAC;KAC5B,CAAC;IACF,cAAc,CAAC,EAAE,cAAc,CAAC;CACjC;AAED,qBAAa,2BAA4B,SAAQ,KAAK;IACpD,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,uBAAuB,EAAE;QAAE,MAAM,EAAE,UAAU,CAAA;KAAE,CAAC,CAAC;gBAE9D,MAAM,EAAE,OAAO,CAAC,uBAAuB,EAAE;QAAE,MAAM,EAAE,UAAU,CAAA;KAAE,CAAC;CAK7E;AAED,MAAM,WAAW,0BAA0B;IACzC,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,SAAS,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC;IAClE,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,KAAK,iBAAiB,GAAG,eAAe,CAAC,eAAe,CAAC,CAAC;AAiE1D,qBAAa,uBAAuB;;gBAQhC,UAAU,EAAE,iBAAiB,EAC7B,OAAO,EAAE,gBAAgB,EACzB,iBAAiB,CAAC,EAAE,IAAI,CAAC,wBAAwB,EAAE,YAAY,CAAC,EAChE,aAAa,CAAC,EAAE,0BAA0B,EAC1C,cAAc,CAAC,EAAE,qBAAqB;IASlC,OAAO,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,0BAA0B,CAAC;CAsHjF"}
1
+ {"version":3,"file":"start-coordinator.d.ts","sourceRoot":"","sources":["../../src/rules/start-coordinator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAI9D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,4CAA4C,CAAC;AACxF,OAAO,KAAK,EAAwB,0BAA0B,EAAE,MAAM,2CAA2C,CAAC;AAClH,OAAO,KAAK,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AACnG,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AACxE,OAAO,KAAK,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAE5E,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,EAAE,MAAM,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE;QAAE,IAAI,EAAE,QAAQ,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,IAAI,EAAE,OAAO,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1G,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE;QACR,EAAE,CAAC,EAAE,MAAM,CAAC;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,mBAAmB,CAAC;KAC5B,CAAC;IACF,cAAc,CAAC,EAAE,cAAc,CAAC;CACjC;AAED,qBAAa,2BAA4B,SAAQ,KAAK;IACpD,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,uBAAuB,EAAE;QAAE,MAAM,EAAE,UAAU,CAAA;KAAE,CAAC,CAAC;gBAE9D,MAAM,EAAE,OAAO,CAAC,uBAAuB,EAAE;QAAE,MAAM,EAAE,UAAU,CAAA;KAAE,CAAC;CAK7E;AAED,MAAM,WAAW,0BAA0B;IACzC,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,SAAS,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC;IAClE,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,KAAK,iBAAiB,GAAG,eAAe,CAAC,eAAe,CAAC,CAAC;AAqD1D,qBAAa,uBAAuB;;gBAQhC,UAAU,EAAE,iBAAiB,EAC7B,OAAO,EAAE,gBAAgB,EACzB,iBAAiB,CAAC,EAAE,IAAI,CAAC,wBAAwB,EAAE,YAAY,CAAC,EAChE,aAAa,CAAC,EAAE,0BAA0B,EAC1C,cAAc,CAAC,EAAE,qBAAqB;IASlC,OAAO,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,0BAA0B,CAAC;CAwGjF"}
@@ -1,3 +1,4 @@
1
+ import { hydrateFactorySession } from "../session/factory-session.js";
1
2
  import { formatSkillActivation } from "@mastra/core/workspace";
2
3
  import { RequestContext } from "@mastra/core/request-context";
3
4
  //#region src/rules/start-coordinator.ts
@@ -51,16 +52,6 @@ async function configureThread(session, request) {
51
52
  })));
52
53
  return threadId;
53
54
  }
54
- async function applyMemorySettings(session, record) {
55
- if (record?.observerModelId) await session.om.observer.switchModel({ modelId: record.observerModelId });
56
- if (record?.reflectorModelId) await session.om.reflector.switchModel({ modelId: record.reflectorModelId });
57
- const state = {
58
- ...record?.observationThreshold != null ? { observationThreshold: record.observationThreshold } : {},
59
- ...record?.reflectionThreshold != null ? { reflectionThreshold: record.reflectionThreshold } : {},
60
- ...record?.observeAttachments != null ? { observeAttachments: record.observeAttachments } : {}
61
- };
62
- if (Object.keys(state).length > 0) await session.state.set(state);
63
- }
64
55
  var FactoryStartCoordinator = class {
65
56
  #controller;
66
57
  #storage;
@@ -105,22 +96,12 @@ var FactoryStartCoordinator = class {
105
96
  ...baseRef ? { baseRef } : {}
106
97
  } : {}
107
98
  });
108
- if (this.#memorySettings) try {
109
- await applyMemorySettings(session, await this.#memorySettings.get({
110
- orgId: request.orgId,
111
- userId: request.userId
112
- }));
113
- } catch (error) {
114
- console.warn("[Factory Start] Failed to apply observational-memory settings", { error: error instanceof Error ? error.message : String(error) });
115
- }
116
- if (request.defaultModelId) try {
117
- await session.model.switch({ modelId: request.defaultModelId });
118
- } catch (error) {
119
- console.warn("[Factory Start] Failed to apply factory default model", {
120
- modelId: request.defaultModelId,
121
- error: error instanceof Error ? error.message : String(error)
122
- });
123
- }
99
+ await hydrateFactorySession(session, {
100
+ orgId: request.orgId,
101
+ userId: request.userId,
102
+ defaultModelId: request.defaultModelId,
103
+ memorySettings: this.#memorySettings
104
+ });
124
105
  const threadId = await configureThread(session, request);
125
106
  const kickoffMessage = await resolveKickoffMessage(session, request.invocation);
126
107
  const prepared = await storage.prepareRunStart({
@@ -1 +1 @@
1
- {"version":3,"file":"start-coordinator.js","names":["#controller","#storage","#transitionService","#sourceControl","#memorySettings"],"sources":["../../src/rules/start-coordinator.ts"],"sourcesContent":["import type { MastraCodeState } from '@mastra/code-sdk/schema';\nimport type { AgentController } from '@mastra/core/agent-controller';\nimport { RequestContext } from '@mastra/core/request-context';\nimport { formatSkillActivation } from '@mastra/core/workspace';\n\nimport type { MemorySettingsRecord, MemorySettingsStorage } from '../storage/domains/memory-settings/base.js';\nimport type { SourceControlSession, SourceControlStorageHandle } from '../storage/domains/source-control/base.js';\nimport type { CreateWorkItemInput, WorkItemsStorage } from '../storage/domains/work-items/base.js';\nimport type { FactoryTransitionService } from './transition-service.js';\nimport type { FactoryRuleStage, FactoryTransitionResult } from './types.js';\n\nexport interface FactoryStartRequest {\n orgId: string;\n userId: string;\n factoryProjectId: string;\n sessionId: string;\n threadTitle: string;\n threadTags?: Record<string, string>;\n kickoffKey: string;\n invocation?: { type: 'prompt'; prompt: string } | { type: 'skill'; skillName: string; arguments: string };\n destinationStage: FactoryRuleStage;\n defaultModelId?: string;\n workItem: {\n id?: string;\n role: string;\n input: CreateWorkItemInput;\n };\n requestContext?: RequestContext;\n}\n\nexport class FactoryStartTransitionError extends Error {\n readonly result: Extract<FactoryTransitionResult, { status: 'rejected' }>;\n\n constructor(result: Extract<FactoryTransitionResult, { status: 'rejected' }>) {\n super(result.reason);\n this.name = 'FactoryStartTransitionError';\n this.result = result;\n }\n}\n\nexport interface FactoryStartPreparedResult {\n workItemId: string;\n bindingId: string;\n threadId: string;\n resourceId: string;\n sessionId: string;\n branch: string;\n revision: number;\n kickoffStatus: 'pending' | 'leased' | 'retry' | 'sent' | 'failed';\n replayed: boolean;\n}\n\ntype FactoryController = AgentController<MastraCodeState>;\ntype FactorySession = Awaited<ReturnType<FactoryController['createSession']>>;\n\nfunction escapeSkillBoundary(value: string): string {\n return value.replaceAll('</skill>', '&lt;/skill&gt;');\n}\n\nasync function resolveKickoffMessage(\n session: FactorySession,\n invocation: FactoryStartRequest['invocation'],\n): Promise<string | null> {\n if (!invocation) return null;\n if (invocation.type === 'prompt') return invocation.prompt;\n\n const skills = session.getWorkspace().skills;\n await skills?.maybeRefresh();\n const skill = await skills?.get(invocation.skillName);\n if (!skill || skill['user-invocable'] === false) {\n throw new Error(`Skill not found: ${invocation.skillName}.`);\n }\n const args = invocation.arguments.trim();\n const content = `${formatSkillActivation(skill)}${args ? `\\n\\nARGUMENTS: ${args}` : ''}`.trim();\n return `<skill name=\"${skill.name}\">\\n${escapeSkillBoundary(content)}\\n</skill>`;\n}\n\nasync function resolveSourceSession(\n storage: SourceControlStorageHandle,\n request: FactoryStartRequest,\n): Promise<SourceControlSession> {\n const session = await storage.sessions.getBySessionId(request.sessionId);\n if (!session || session.orgId !== request.orgId || session.userId !== request.userId) {\n throw new Error('Factory session not found');\n }\n const projectRepository = await storage.projectRepositories.get({\n orgId: request.orgId,\n id: session.projectRepositoryId,\n });\n if (!projectRepository) throw new Error('Factory session repository not found');\n const connection = await storage.connections.get({ orgId: request.orgId, id: projectRepository.connectionId });\n if (!connection || connection.factoryProjectId !== request.factoryProjectId) {\n throw new Error('Factory session does not belong to this project');\n }\n return session;\n}\n\nasync function configureThread(session: FactorySession, request: FactoryStartRequest): Promise<string> {\n const threadId = session.thread.requireId();\n await session.thread.rename({ title: request.threadTitle });\n const settings = { ...(request.threadTags ?? {}), factorySessionId: request.sessionId };\n await Promise.all(Object.entries(settings).map(([key, value]) => session.thread.setSetting({ key, value })));\n return threadId;\n}\n\nasync function applyMemorySettings(session: FactorySession, record: MemorySettingsRecord | null): Promise<void> {\n if (record?.observerModelId) await session.om.observer.switchModel({ modelId: record.observerModelId });\n if (record?.reflectorModelId) await session.om.reflector.switchModel({ modelId: record.reflectorModelId });\n\n const state = {\n ...(record?.observationThreshold != null ? { observationThreshold: record.observationThreshold } : {}),\n ...(record?.reflectionThreshold != null ? { reflectionThreshold: record.reflectionThreshold } : {}),\n ...(record?.observeAttachments != null ? { observeAttachments: record.observeAttachments } : {}),\n };\n if (Object.keys(state).length > 0) await session.state.set(state);\n}\n\nexport class FactoryStartCoordinator {\n readonly #controller: FactoryController;\n readonly #storage: WorkItemsStorage;\n readonly #transitionService?: Pick<FactoryTransitionService, 'transition'>;\n readonly #sourceControl?: SourceControlStorageHandle;\n readonly #memorySettings?: MemorySettingsStorage;\n\n constructor(\n controller: FactoryController,\n storage: WorkItemsStorage,\n transitionService?: Pick<FactoryTransitionService, 'transition'>,\n sourceControl?: SourceControlStorageHandle,\n memorySettings?: MemorySettingsStorage,\n ) {\n this.#controller = controller;\n this.#storage = storage;\n this.#transitionService = transitionService;\n this.#sourceControl = sourceControl;\n this.#memorySettings = memorySettings;\n }\n\n async prepare(request: FactoryStartRequest): Promise<FactoryStartPreparedResult> {\n const storage = this.#storage;\n if (!this.#sourceControl) throw new Error('Factory source control storage is unavailable');\n const sourceSession = await resolveSourceSession(this.#sourceControl, request);\n const requestContext = request.requestContext ?? new RequestContext();\n if (!request.requestContext) {\n requestContext.set('user', { workosId: request.userId, organizationId: request.orgId });\n }\n // Sessions kicked off against third-party content (a PR under review, or\n // any pull-request-sourced work item) get `untrustedCheckout` so the SDK\n // never ingests the checkout's AGENTS.md/CLAUDE.md into the system prompt\n // or reminders — those files are attacker-writable in a PR branch.\n const untrustedCheckout =\n request.workItem.input.externalSource?.type === 'pull-request' ||\n (request.invocation?.type === 'skill' && request.invocation.skillName === 'factory-review');\n // The trusted ref the SDK may serve project instruction files from on an\n // untrusted checkout (the PR's base branch). Prefer the session record's\n // base branch; fall back to the intake metadata captured from the PR.\n const metadataBaseBranch = request.workItem.input.metadata?.baseBranch;\n const baseRef =\n (sourceSession.baseBranch || undefined) ??\n (typeof metadataBaseBranch === 'string' && metadataBaseBranch ? metadataBaseBranch : undefined);\n const sessionTags = {\n factoryProjectId: request.factoryProjectId,\n projectRepositoryId: sourceSession.projectRepositoryId,\n };\n const session = await this.#controller.createSession({\n id: sourceSession.sessionId,\n ownerId: request.userId,\n resourceId: sourceSession.sessionId,\n threadId: sourceSession.sessionId,\n requestContext,\n tags: sessionTags,\n });\n // Bound-agent authority gates (the transition tool, the factory-phase\n // processor, workspace token selection) resolve the session address from\n // controller state. Seed it server-side — `tags` covers fresh creation,\n // the explicit setState covers get-or-create returning a session another\n // caller created without them — so autonomous runs never depend on a\n // browser connecting to populate the state. `untrustedCheckout` is a\n // boolean so it rides only on state (tags are string-valued).\n await session.state.set({\n ...sessionTags,\n ...(untrustedCheckout ? { untrustedCheckout: true, ...(baseRef ? { baseRef } : {}) } : {}),\n });\n if (this.#memorySettings) {\n try {\n const record = await this.#memorySettings.get({ orgId: request.orgId, userId: request.userId });\n await applyMemorySettings(session, record);\n } catch (error) {\n console.warn('[Factory Start] Failed to apply observational-memory settings', {\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n if (request.defaultModelId) {\n try {\n await session.model.switch({ modelId: request.defaultModelId });\n } catch (error) {\n console.warn('[Factory Start] Failed to apply factory default model', {\n modelId: request.defaultModelId,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n const threadId = await configureThread(session, request);\n const kickoffMessage = await resolveKickoffMessage(session, request.invocation);\n const prepared = await storage.prepareRunStart({\n orgId: request.orgId,\n userId: request.userId,\n factoryProjectId: request.factoryProjectId,\n workItem: { id: request.workItem.id, input: request.workItem.input },\n role: request.workItem.role,\n session: { sessionId: sourceSession.sessionId, branch: sourceSession.branch, threadId },\n resourceId: sourceSession.sessionId,\n kickoffKey: request.kickoffKey,\n kickoffMessage,\n });\n await session.thread.setSetting({ key: 'factoryWorkItemId', value: prepared.item.id });\n\n let revision = prepared.item.revision;\n if (prepared.item.stages.length !== 1 || prepared.item.stages[0] !== request.destinationStage) {\n if (!this.#transitionService) throw new Error('Factory transition service is unavailable.');\n const transition = await this.#transitionService.transition({\n orgId: request.orgId,\n factoryProjectId: request.factoryProjectId,\n workItemId: prepared.item.id,\n board: prepared.item.externalSource?.type === 'pull-request' ? 'review' : 'work',\n stage: request.destinationStage,\n expectedRevision: prepared.item.revision,\n actor: { type: 'human', id: request.userId },\n ingress: { type: 'human', identity: `start:${request.kickoffKey}:transition` },\n cause: 'run_start',\n });\n if (transition.status === 'rejected') {\n await storage.markPendingStart(prepared.binding.id, 'failed', transition.reason);\n throw new FactoryStartTransitionError(transition);\n }\n revision = transition.revision;\n }\n\n if (kickoffMessage === null) {\n await storage.markPendingStart(prepared.binding.id, 'sent');\n prepared.pendingStart.status = 'sent';\n }\n\n return {\n workItemId: prepared.item.id,\n bindingId: prepared.binding.id,\n threadId,\n resourceId: sourceSession.sessionId,\n sessionId: sourceSession.sessionId,\n branch: sourceSession.branch,\n revision,\n kickoffStatus: prepared.pendingStart.status,\n replayed: prepared.replayed,\n };\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"start-coordinator.js","names":["#controller","#storage","#transitionService","#sourceControl","#memorySettings"],"sources":["../../src/rules/start-coordinator.ts"],"sourcesContent":["import type { MastraCodeState } from '@mastra/code-sdk/schema';\nimport type { AgentController } from '@mastra/core/agent-controller';\nimport { RequestContext } from '@mastra/core/request-context';\nimport { formatSkillActivation } from '@mastra/core/workspace';\n\nimport { hydrateFactorySession } from '../session/factory-session.js';\nimport type { MemorySettingsStorage } from '../storage/domains/memory-settings/base.js';\nimport type { SourceControlSession, SourceControlStorageHandle } from '../storage/domains/source-control/base.js';\nimport type { CreateWorkItemInput, WorkItemsStorage } from '../storage/domains/work-items/base.js';\nimport type { FactoryTransitionService } from './transition-service.js';\nimport type { FactoryRuleStage, FactoryTransitionResult } from './types.js';\n\nexport interface FactoryStartRequest {\n orgId: string;\n userId: string;\n factoryProjectId: string;\n sessionId: string;\n threadTitle: string;\n threadTags?: Record<string, string>;\n kickoffKey: string;\n invocation?: { type: 'prompt'; prompt: string } | { type: 'skill'; skillName: string; arguments: string };\n destinationStage: FactoryRuleStage;\n defaultModelId?: string;\n workItem: {\n id?: string;\n role: string;\n input: CreateWorkItemInput;\n };\n requestContext?: RequestContext;\n}\n\nexport class FactoryStartTransitionError extends Error {\n readonly result: Extract<FactoryTransitionResult, { status: 'rejected' }>;\n\n constructor(result: Extract<FactoryTransitionResult, { status: 'rejected' }>) {\n super(result.reason);\n this.name = 'FactoryStartTransitionError';\n this.result = result;\n }\n}\n\nexport interface FactoryStartPreparedResult {\n workItemId: string;\n bindingId: string;\n threadId: string;\n resourceId: string;\n sessionId: string;\n branch: string;\n revision: number;\n kickoffStatus: 'pending' | 'leased' | 'retry' | 'sent' | 'failed';\n replayed: boolean;\n}\n\ntype FactoryController = AgentController<MastraCodeState>;\ntype FactorySession = Awaited<ReturnType<FactoryController['createSession']>>;\n\nfunction escapeSkillBoundary(value: string): string {\n return value.replaceAll('</skill>', '&lt;/skill&gt;');\n}\n\nasync function resolveKickoffMessage(\n session: FactorySession,\n invocation: FactoryStartRequest['invocation'],\n): Promise<string | null> {\n if (!invocation) return null;\n if (invocation.type === 'prompt') return invocation.prompt;\n\n const skills = session.getWorkspace().skills;\n await skills?.maybeRefresh();\n const skill = await skills?.get(invocation.skillName);\n if (!skill || skill['user-invocable'] === false) {\n throw new Error(`Skill not found: ${invocation.skillName}.`);\n }\n const args = invocation.arguments.trim();\n const content = `${formatSkillActivation(skill)}${args ? `\\n\\nARGUMENTS: ${args}` : ''}`.trim();\n return `<skill name=\"${skill.name}\">\\n${escapeSkillBoundary(content)}\\n</skill>`;\n}\n\nasync function resolveSourceSession(\n storage: SourceControlStorageHandle,\n request: FactoryStartRequest,\n): Promise<SourceControlSession> {\n const session = await storage.sessions.getBySessionId(request.sessionId);\n if (!session || session.orgId !== request.orgId || session.userId !== request.userId) {\n throw new Error('Factory session not found');\n }\n const projectRepository = await storage.projectRepositories.get({\n orgId: request.orgId,\n id: session.projectRepositoryId,\n });\n if (!projectRepository) throw new Error('Factory session repository not found');\n const connection = await storage.connections.get({ orgId: request.orgId, id: projectRepository.connectionId });\n if (!connection || connection.factoryProjectId !== request.factoryProjectId) {\n throw new Error('Factory session does not belong to this project');\n }\n return session;\n}\n\nasync function configureThread(session: FactorySession, request: FactoryStartRequest): Promise<string> {\n const threadId = session.thread.requireId();\n await session.thread.rename({ title: request.threadTitle });\n const settings = { ...(request.threadTags ?? {}), factorySessionId: request.sessionId };\n await Promise.all(Object.entries(settings).map(([key, value]) => session.thread.setSetting({ key, value })));\n return threadId;\n}\n\nexport class FactoryStartCoordinator {\n readonly #controller: FactoryController;\n readonly #storage: WorkItemsStorage;\n readonly #transitionService?: Pick<FactoryTransitionService, 'transition'>;\n readonly #sourceControl?: SourceControlStorageHandle;\n readonly #memorySettings?: MemorySettingsStorage;\n\n constructor(\n controller: FactoryController,\n storage: WorkItemsStorage,\n transitionService?: Pick<FactoryTransitionService, 'transition'>,\n sourceControl?: SourceControlStorageHandle,\n memorySettings?: MemorySettingsStorage,\n ) {\n this.#controller = controller;\n this.#storage = storage;\n this.#transitionService = transitionService;\n this.#sourceControl = sourceControl;\n this.#memorySettings = memorySettings;\n }\n\n async prepare(request: FactoryStartRequest): Promise<FactoryStartPreparedResult> {\n const storage = this.#storage;\n if (!this.#sourceControl) throw new Error('Factory source control storage is unavailable');\n const sourceSession = await resolveSourceSession(this.#sourceControl, request);\n const requestContext = request.requestContext ?? new RequestContext();\n if (!request.requestContext) {\n requestContext.set('user', { workosId: request.userId, organizationId: request.orgId });\n }\n // Sessions kicked off against third-party content (a PR under review, or\n // any pull-request-sourced work item) get `untrustedCheckout` so the SDK\n // never ingests the checkout's AGENTS.md/CLAUDE.md into the system prompt\n // or reminders — those files are attacker-writable in a PR branch.\n const untrustedCheckout =\n request.workItem.input.externalSource?.type === 'pull-request' ||\n (request.invocation?.type === 'skill' && request.invocation.skillName === 'factory-review');\n // The trusted ref the SDK may serve project instruction files from on an\n // untrusted checkout (the PR's base branch). Prefer the session record's\n // base branch; fall back to the intake metadata captured from the PR.\n const metadataBaseBranch = request.workItem.input.metadata?.baseBranch;\n const baseRef =\n (sourceSession.baseBranch || undefined) ??\n (typeof metadataBaseBranch === 'string' && metadataBaseBranch ? metadataBaseBranch : undefined);\n const sessionTags = {\n factoryProjectId: request.factoryProjectId,\n projectRepositoryId: sourceSession.projectRepositoryId,\n };\n const session = await this.#controller.createSession({\n id: sourceSession.sessionId,\n ownerId: request.userId,\n resourceId: sourceSession.sessionId,\n threadId: sourceSession.sessionId,\n requestContext,\n tags: sessionTags,\n });\n // Bound-agent authority gates (the transition tool, the factory-phase\n // processor, workspace token selection) resolve the session address from\n // controller state. Seed it server-side — `tags` covers fresh creation,\n // the explicit setState covers get-or-create returning a session another\n // caller created without them — so autonomous runs never depend on a\n // browser connecting to populate the state. `untrustedCheckout` is a\n // boolean so it rides only on state (tags are string-valued).\n await session.state.set({\n ...sessionTags,\n ...(untrustedCheckout ? { untrustedCheckout: true, ...(baseRef ? { baseRef } : {}) } : {}),\n });\n await hydrateFactorySession(session, {\n orgId: request.orgId,\n userId: request.userId,\n defaultModelId: request.defaultModelId,\n memorySettings: this.#memorySettings,\n });\n const threadId = await configureThread(session, request);\n const kickoffMessage = await resolveKickoffMessage(session, request.invocation);\n const prepared = await storage.prepareRunStart({\n orgId: request.orgId,\n userId: request.userId,\n factoryProjectId: request.factoryProjectId,\n workItem: { id: request.workItem.id, input: request.workItem.input },\n role: request.workItem.role,\n session: { sessionId: sourceSession.sessionId, branch: sourceSession.branch, threadId },\n resourceId: sourceSession.sessionId,\n kickoffKey: request.kickoffKey,\n kickoffMessage,\n });\n await session.thread.setSetting({ key: 'factoryWorkItemId', value: prepared.item.id });\n\n let revision = prepared.item.revision;\n if (prepared.item.stages.length !== 1 || prepared.item.stages[0] !== request.destinationStage) {\n if (!this.#transitionService) throw new Error('Factory transition service is unavailable.');\n const transition = await this.#transitionService.transition({\n orgId: request.orgId,\n factoryProjectId: request.factoryProjectId,\n workItemId: prepared.item.id,\n board: prepared.item.externalSource?.type === 'pull-request' ? 'review' : 'work',\n stage: request.destinationStage,\n expectedRevision: prepared.item.revision,\n actor: { type: 'human', id: request.userId },\n ingress: { type: 'human', identity: `start:${request.kickoffKey}:transition` },\n cause: 'run_start',\n });\n if (transition.status === 'rejected') {\n await storage.markPendingStart(prepared.binding.id, 'failed', transition.reason);\n throw new FactoryStartTransitionError(transition);\n }\n revision = transition.revision;\n }\n\n if (kickoffMessage === null) {\n await storage.markPendingStart(prepared.binding.id, 'sent');\n prepared.pendingStart.status = 'sent';\n }\n\n return {\n workItemId: prepared.item.id,\n bindingId: prepared.binding.id,\n threadId,\n resourceId: sourceSession.sessionId,\n sessionId: sourceSession.sessionId,\n branch: sourceSession.branch,\n revision,\n kickoffStatus: prepared.pendingStart.status,\n replayed: prepared.replayed,\n };\n }\n}\n"],"mappings":"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"}
@@ -1,10 +1,12 @@
1
1
  import type { RequestContext } from '@mastra/core/request-context';
2
2
  import type { IntegrationTools } from '../integrations/base.js';
3
3
  import type { WorkItemsStorage } from '../storage/domains/work-items/base.js';
4
+ import type { FactorySessionSourceLookup } from './binding-context.js';
4
5
  import type { FactoryTransitionService } from './transition-service.js';
5
6
  export declare function createFactoryTransitionTools(options: {
6
7
  requestContext: RequestContext;
7
8
  storage: WorkItemsStorage;
8
9
  transitionService: Pick<FactoryTransitionService, 'transition'>;
10
+ sessions?: FactorySessionSourceLookup;
9
11
  }): Promise<IntegrationTools>;
10
12
  //# sourceMappingURL=tools.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/rules/tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAInE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AAE9E,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AAgBxE,wBAAsB,4BAA4B,CAAC,OAAO,EAAE;IAC1D,cAAc,EAAE,cAAc,CAAC;IAC/B,OAAO,EAAE,gBAAgB,CAAC;IAC1B,iBAAiB,EAAE,IAAI,CAAC,wBAAwB,EAAE,YAAY,CAAC,CAAC;CACjE,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAwC5B"}
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/rules/tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAInE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AAC9E,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,sBAAsB,CAAC;AAEvE,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AA0BxE,wBAAsB,4BAA4B,CAAC,OAAO,EAAE;IAC1D,cAAc,EAAE,cAAc,CAAC;IAC/B,OAAO,EAAE,gBAAgB,CAAC;IAC1B,iBAAiB,EAAE,IAAI,CAAC,wBAAwB,EAAE,YAAY,CAAC,CAAC;IAChE,QAAQ,CAAC,EAAE,0BAA0B,CAAC;CACvC,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAiD5B"}
@@ -1,20 +1,25 @@
1
1
  import { FACTORY_RULE_STAGES } from "./types.js";
2
- import { getFactorySessionAddress } from "./binding-context.js";
2
+ import { resolveFactorySessionAddress } from "./binding-context.js";
3
3
  import { createTool } from "@mastra/core/tools";
4
4
  import { z } from "zod";
5
5
  //#region src/rules/tools.ts
6
+ const MAX_RATIONALE_LENGTH = 1e3;
6
7
  const transitionInputSchema = z.object({
7
8
  stage: z.enum(FACTORY_RULE_STAGES),
8
9
  expectedRevision: z.number().int().positive(),
9
- rationale: z.string().trim().min(1).max(1e3)
10
+ rationale: z.string().trim().min(1).transform((value) => value.length <= MAX_RATIONALE_LENGTH ? value : `${value.slice(0, MAX_RATIONALE_LENGTH - 1)}…`)
10
11
  }).strict();
11
12
  function boardForSource(type) {
12
13
  return type === "pull-request" ? "review" : "work";
13
14
  }
14
15
  async function createFactoryTransitionTools(options) {
15
- const address = getFactorySessionAddress(options.requestContext);
16
- if (!address) return {};
17
- const availableBinding = await options.storage.findActiveRunBinding(address);
16
+ const resolution = await resolveFactorySessionAddress({
17
+ requestContext: options.requestContext,
18
+ storage: options.storage,
19
+ sessions: options.sessions
20
+ });
21
+ if (!resolution) return {};
22
+ const availableBinding = resolution.binding ?? await options.storage.findActiveRunBinding(resolution.address);
18
23
  if (!availableBinding) return {};
19
24
  return { factory_transition_work_item: createTool({
20
25
  id: "factory_transition_work_item",
@@ -22,7 +27,11 @@ async function createFactoryTransitionTools(options) {
22
27
  inputSchema: transitionInputSchema,
23
28
  requireApproval: true,
24
29
  execute: async ({ stage, expectedRevision, rationale }, execution) => {
25
- const currentAddress = getFactorySessionAddress(execution.requestContext);
30
+ const currentAddress = (await resolveFactorySessionAddress({
31
+ requestContext: execution.requestContext,
32
+ storage: options.storage,
33
+ sessions: options.sessions
34
+ }))?.address ?? null;
26
35
  const toolCallId = execution.agent?.toolCallId;
27
36
  if (!currentAddress || !toolCallId) throw new Error("Factory transitions require an authenticated bound agent tool call.");
28
37
  const binding = await options.storage.findActiveRunBinding(currentAddress);
@@ -1 +1 @@
1
- {"version":3,"file":"tools.js","names":[],"sources":["../../src/rules/tools.ts"],"sourcesContent":["import type { RequestContext } from '@mastra/core/request-context';\nimport { createTool } from '@mastra/core/tools';\nimport { z } from 'zod';\n\nimport type { IntegrationTools } from '../integrations/base.js';\nimport type { WorkItemsStorage } from '../storage/domains/work-items/base.js';\nimport { getFactorySessionAddress } from './binding-context.js';\nimport type { FactoryTransitionService } from './transition-service.js';\nimport { FACTORY_RULE_STAGES } from './types.js';\nimport type { FactoryRuleBoard } from './types.js';\n\nconst transitionInputSchema = z\n .object({\n stage: z.enum(FACTORY_RULE_STAGES),\n expectedRevision: z.number().int().positive(),\n rationale: z.string().trim().min(1).max(1_000),\n })\n .strict();\n\nfunction boardForSource(type: string | undefined): FactoryRuleBoard {\n return type === 'pull-request' ? 'review' : 'work';\n}\n\nexport async function createFactoryTransitionTools(options: {\n requestContext: RequestContext;\n storage: WorkItemsStorage;\n transitionService: Pick<FactoryTransitionService, 'transition'>;\n}): Promise<IntegrationTools> {\n const address = getFactorySessionAddress(options.requestContext);\n if (!address) return {};\n const availableBinding = await options.storage.findActiveRunBinding(address);\n if (!availableBinding) return {};\n\n return {\n factory_transition_work_item: createTool({\n id: 'factory_transition_work_item',\n description:\n 'Request a governed stage transition for the Factory work item exactly bound to this thread. Use the current revision from the factory-phase signal and explain why the transition is appropriate.',\n inputSchema: transitionInputSchema,\n requireApproval: true,\n execute: async ({ stage, expectedRevision, rationale }, execution) => {\n const currentAddress = getFactorySessionAddress(execution.requestContext);\n const toolCallId = execution.agent?.toolCallId;\n if (!currentAddress || !toolCallId) {\n throw new Error('Factory transitions require an authenticated bound agent tool call.');\n }\n const binding = await options.storage.findActiveRunBinding(currentAddress);\n if (!binding || binding.id !== availableBinding.id) {\n throw new Error('Factory agent binding is unavailable, revoked, or no longer matches this session.');\n }\n const item = await options.storage.get({ orgId: binding.orgId, id: binding.workItemId });\n if (!item) throw new Error('Bound Factory work item not found.');\n\n return options.transitionService.transition({\n orgId: binding.orgId,\n factoryProjectId: binding.factoryProjectId,\n workItemId: binding.workItemId,\n board: boardForSource(item.externalSource?.type),\n stage,\n expectedRevision,\n actor: { type: 'agent', bindingId: binding.id, role: binding.role },\n ingress: { type: 'agent', identity: `${binding.id}:${toolCallId}` },\n cause: rationale,\n });\n },\n }),\n };\n}\n"],"mappings":";;;;;AAWA,MAAM,wBAAwB,EAC3B,OAAO;CACN,OAAO,EAAE,KAAK,mBAAmB;CACjC,kBAAkB,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS;CAC5C,WAAW,EAAE,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAAK;AAC/C,CAAC,CAAC,CACD,OAAO;AAEV,SAAS,eAAe,MAA4C;CAClE,OAAO,SAAS,iBAAiB,WAAW;AAC9C;AAEA,eAAsB,6BAA6B,SAIrB;CAC5B,MAAM,UAAU,yBAAyB,QAAQ,cAAc;CAC/D,IAAI,CAAC,SAAS,OAAO,CAAC;CACtB,MAAM,mBAAmB,MAAM,QAAQ,QAAQ,qBAAqB,OAAO;CAC3E,IAAI,CAAC,kBAAkB,OAAO,CAAC;CAE/B,OAAO,EACL,8BAA8B,WAAW;EACvC,IAAI;EACJ,aACE;EACF,aAAa;EACb,iBAAiB;EACjB,SAAS,OAAO,EAAE,OAAO,kBAAkB,aAAa,cAAc;GACpE,MAAM,iBAAiB,yBAAyB,UAAU,cAAc;GACxE,MAAM,aAAa,UAAU,OAAO;GACpC,IAAI,CAAC,kBAAkB,CAAC,YACtB,MAAM,IAAI,MAAM,qEAAqE;GAEvF,MAAM,UAAU,MAAM,QAAQ,QAAQ,qBAAqB,cAAc;GACzE,IAAI,CAAC,WAAW,QAAQ,OAAO,iBAAiB,IAC9C,MAAM,IAAI,MAAM,mFAAmF;GAErG,MAAM,OAAO,MAAM,QAAQ,QAAQ,IAAI;IAAE,OAAO,QAAQ;IAAO,IAAI,QAAQ;GAAW,CAAC;GACvF,IAAI,CAAC,MAAM,MAAM,IAAI,MAAM,oCAAoC;GAE/D,OAAO,QAAQ,kBAAkB,WAAW;IAC1C,OAAO,QAAQ;IACf,kBAAkB,QAAQ;IAC1B,YAAY,QAAQ;IACpB,OAAO,eAAe,KAAK,gBAAgB,IAAI;IAC/C;IACA;IACA,OAAO;KAAE,MAAM;KAAS,WAAW,QAAQ;KAAI,MAAM,QAAQ;IAAK;IAClE,SAAS;KAAE,MAAM;KAAS,UAAU,GAAG,QAAQ,GAAG,GAAG;IAAa;IAClE,OAAO;GACT,CAAC;EACH;CACF,CAAC,EACH;AACF"}
1
+ {"version":3,"file":"tools.js","names":[],"sources":["../../src/rules/tools.ts"],"sourcesContent":["import type { RequestContext } from '@mastra/core/request-context';\nimport { createTool } from '@mastra/core/tools';\nimport { z } from 'zod';\n\nimport type { IntegrationTools } from '../integrations/base.js';\nimport type { WorkItemsStorage } from '../storage/domains/work-items/base.js';\nimport type { FactorySessionSourceLookup } from './binding-context.js';\nimport { resolveFactorySessionAddress } from './binding-context.js';\nimport type { FactoryTransitionService } from './transition-service.js';\nimport { FACTORY_RULE_STAGES } from './types.js';\nimport type { FactoryRuleBoard } from './types.js';\n\nconst MAX_RATIONALE_LENGTH = 1_000;\n\nconst transitionInputSchema = z\n .object({\n stage: z.enum(FACTORY_RULE_STAGES),\n expectedRevision: z.number().int().positive(),\n // Providers strip maxLength from the JSON schema and models can't count characters, so a\n // hard cap invites overshoot-retry loops at the end of every run. Accept and clamp instead.\n rationale: z\n .string()\n .trim()\n .min(1)\n .transform(value =>\n value.length <= MAX_RATIONALE_LENGTH ? value : `${value.slice(0, MAX_RATIONALE_LENGTH - 1)}…`,\n ),\n })\n .strict();\n\nfunction boardForSource(type: string | undefined): FactoryRuleBoard {\n return type === 'pull-request' ? 'review' : 'work';\n}\n\nexport async function createFactoryTransitionTools(options: {\n requestContext: RequestContext;\n storage: WorkItemsStorage;\n transitionService: Pick<FactoryTransitionService, 'transition'>;\n sessions?: FactorySessionSourceLookup;\n}): Promise<IntegrationTools> {\n const resolution = await resolveFactorySessionAddress({\n requestContext: options.requestContext,\n storage: options.storage,\n sessions: options.sessions,\n });\n if (!resolution) return {};\n const availableBinding = resolution.binding ?? (await options.storage.findActiveRunBinding(resolution.address));\n if (!availableBinding) return {};\n\n return {\n factory_transition_work_item: createTool({\n id: 'factory_transition_work_item',\n description:\n 'Request a governed stage transition for the Factory work item exactly bound to this thread. Use the current revision from the factory-phase signal and explain why the transition is appropriate.',\n inputSchema: transitionInputSchema,\n requireApproval: true,\n execute: async ({ stage, expectedRevision, rationale }, execution) => {\n const currentResolution = await resolveFactorySessionAddress({\n requestContext: execution.requestContext,\n storage: options.storage,\n sessions: options.sessions,\n });\n const currentAddress = currentResolution?.address ?? null;\n const toolCallId = execution.agent?.toolCallId;\n if (!currentAddress || !toolCallId) {\n throw new Error('Factory transitions require an authenticated bound agent tool call.');\n }\n const binding = await options.storage.findActiveRunBinding(currentAddress);\n if (!binding || binding.id !== availableBinding.id) {\n throw new Error('Factory agent binding is unavailable, revoked, or no longer matches this session.');\n }\n const item = await options.storage.get({ orgId: binding.orgId, id: binding.workItemId });\n if (!item) throw new Error('Bound Factory work item not found.');\n\n return options.transitionService.transition({\n orgId: binding.orgId,\n factoryProjectId: binding.factoryProjectId,\n workItemId: binding.workItemId,\n board: boardForSource(item.externalSource?.type),\n stage,\n expectedRevision,\n actor: { type: 'agent', bindingId: binding.id, role: binding.role },\n ingress: { type: 'agent', identity: `${binding.id}:${toolCallId}` },\n cause: rationale,\n });\n },\n }),\n };\n}\n"],"mappings":";;;;;AAYA,MAAM,uBAAuB;AAE7B,MAAM,wBAAwB,EAC3B,OAAO;CACN,OAAO,EAAE,KAAK,mBAAmB;CACjC,kBAAkB,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS;CAG5C,WAAW,EACR,OAAO,CAAC,CACR,KAAK,CAAC,CACN,IAAI,CAAC,CAAC,CACN,WAAU,UACT,MAAM,UAAU,uBAAuB,QAAQ,GAAG,MAAM,MAAM,GAAG,uBAAuB,CAAC,EAAE,EAC7F;AACJ,CAAC,CAAC,CACD,OAAO;AAEV,SAAS,eAAe,MAA4C;CAClE,OAAO,SAAS,iBAAiB,WAAW;AAC9C;AAEA,eAAsB,6BAA6B,SAKrB;CAC5B,MAAM,aAAa,MAAM,6BAA6B;EACpD,gBAAgB,QAAQ;EACxB,SAAS,QAAQ;EACjB,UAAU,QAAQ;CACpB,CAAC;CACD,IAAI,CAAC,YAAY,OAAO,CAAC;CACzB,MAAM,mBAAmB,WAAW,WAAY,MAAM,QAAQ,QAAQ,qBAAqB,WAAW,OAAO;CAC7G,IAAI,CAAC,kBAAkB,OAAO,CAAC;CAE/B,OAAO,EACL,8BAA8B,WAAW;EACvC,IAAI;EACJ,aACE;EACF,aAAa;EACb,iBAAiB;EACjB,SAAS,OAAO,EAAE,OAAO,kBAAkB,aAAa,cAAc;GAMpE,MAAM,kBAAiB,MALS,6BAA6B;IAC3D,gBAAgB,UAAU;IAC1B,SAAS,QAAQ;IACjB,UAAU,QAAQ;GACpB,CAAC,EAAA,EACyC,WAAW;GACrD,MAAM,aAAa,UAAU,OAAO;GACpC,IAAI,CAAC,kBAAkB,CAAC,YACtB,MAAM,IAAI,MAAM,qEAAqE;GAEvF,MAAM,UAAU,MAAM,QAAQ,QAAQ,qBAAqB,cAAc;GACzE,IAAI,CAAC,WAAW,QAAQ,OAAO,iBAAiB,IAC9C,MAAM,IAAI,MAAM,mFAAmF;GAErG,MAAM,OAAO,MAAM,QAAQ,QAAQ,IAAI;IAAE,OAAO,QAAQ;IAAO,IAAI,QAAQ;GAAW,CAAC;GACvF,IAAI,CAAC,MAAM,MAAM,IAAI,MAAM,oCAAoC;GAE/D,OAAO,QAAQ,kBAAkB,WAAW;IAC1C,OAAO,QAAQ;IACf,kBAAkB,QAAQ;IAC1B,YAAY,QAAQ;IACpB,OAAO,eAAe,KAAK,gBAAgB,IAAI;IAC/C;IACA;IACA,OAAO;KAAE,MAAM;KAAS,WAAW,QAAQ;KAAI,MAAM,QAAQ;IAAK;IAClE,SAAS;KAAE,MAAM;KAAS,UAAU,GAAG,QAAQ,GAAG,GAAG;IAAa;IAClE,OAAO;GACT,CAAC;EACH;CACF,CAAC,EACH;AACF"}