@mastra/factory 0.13.0-alpha.16 → 0.13.0-alpha.17

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 (67) hide show
  1. package/README.md +123 -1
  2. package/dist/auth.d.ts.map +1 -1
  3. package/dist/auth.js +2 -0
  4. package/dist/auth.js.map +1 -1
  5. package/dist/boards/define-board.d.ts.map +1 -1
  6. package/dist/boards/define-board.js +9 -2
  7. package/dist/boards/define-board.js.map +1 -1
  8. package/dist/boards/index.d.ts +1 -1
  9. package/dist/boards/review.d.ts +1 -1
  10. package/dist/boards/work.d.ts +1 -1
  11. package/dist/boards/work.js +1 -1
  12. package/dist/factory.d.ts.map +1 -1
  13. package/dist/factory.js +7 -0
  14. package/dist/factory.js.map +1 -1
  15. package/dist/integrations/github/rules.d.ts.map +1 -1
  16. package/dist/integrations/github/rules.js +7 -4
  17. package/dist/integrations/github/rules.js.map +1 -1
  18. package/dist/integrations/linear/issue-reconciler.d.ts.map +1 -1
  19. package/dist/integrations/linear/issue-reconciler.js +1 -0
  20. package/dist/integrations/linear/issue-reconciler.js.map +1 -1
  21. package/dist/integrations/linear/rules.d.ts +2 -0
  22. package/dist/integrations/linear/rules.d.ts.map +1 -1
  23. package/dist/integrations/linear/rules.js +9 -3
  24. package/dist/integrations/linear/rules.js.map +1 -1
  25. package/dist/routes/contracts.d.ts +21 -43
  26. package/dist/routes/contracts.d.ts.map +1 -1
  27. package/dist/routes/contracts.js +3 -10
  28. package/dist/routes/contracts.js.map +1 -1
  29. package/dist/routes/telemetry.d.ts +13 -0
  30. package/dist/routes/telemetry.d.ts.map +1 -0
  31. package/dist/routes/telemetry.js +60 -0
  32. package/dist/routes/telemetry.js.map +1 -0
  33. package/dist/rules/dispatcher.d.ts.map +1 -1
  34. package/dist/rules/dispatcher.js +23 -6
  35. package/dist/rules/dispatcher.js.map +1 -1
  36. package/dist/rules/index.js +1 -1
  37. package/dist/rules/processor.d.ts.map +1 -1
  38. package/dist/rules/processor.js +17 -22
  39. package/dist/rules/processor.js.map +1 -1
  40. package/dist/rules/tools.d.ts.map +1 -1
  41. package/dist/rules/tools.js +18 -7
  42. package/dist/rules/tools.js.map +1 -1
  43. package/dist/rules/transition-service.d.ts.map +1 -1
  44. package/dist/rules/transition-service.js +2 -1
  45. package/dist/rules/transition-service.js.map +1 -1
  46. package/dist/rules/validation.d.ts +5 -0
  47. package/dist/rules/validation.d.ts.map +1 -1
  48. package/dist/rules/validation.js +21 -6
  49. package/dist/rules/validation.js.map +1 -1
  50. package/dist/storage/domains/work-items/base.d.ts +5 -0
  51. package/dist/storage/domains/work-items/base.d.ts.map +1 -1
  52. package/dist/storage/domains/work-items/base.js +8 -0
  53. package/dist/storage/domains/work-items/base.js.map +1 -1
  54. package/dist/supervisor/health.js +1 -1
  55. package/dist/supervisor/write-tools.d.ts.map +1 -1
  56. package/dist/supervisor/write-tools.js +6 -4
  57. package/dist/supervisor/write-tools.js.map +1 -1
  58. package/dist/telemetry-types.d.ts +7 -0
  59. package/dist/telemetry-types.d.ts.map +1 -0
  60. package/dist/telemetry-types.js +24 -0
  61. package/dist/telemetry-types.js.map +1 -0
  62. package/dist/telemetry.d.ts +9 -0
  63. package/dist/telemetry.d.ts.map +1 -0
  64. package/dist/telemetry.js +47 -0
  65. package/dist/telemetry.js.map +1 -0
  66. package/factory-skills/configure-factory-rules/SKILL.md +13 -1
  67. package/package.json +7 -6
@@ -1,5 +1,5 @@
1
- import { NEEDS_APPROVAL_LABEL, factoryRuleStage } from "../rules/types.js";
2
1
  import { boardForWorkItem, workItemPhaseSemantics } from "../boards/semantics.js";
2
+ import { NEEDS_APPROVAL_LABEL, factoryRuleStage } from "../rules/types.js";
3
3
  import "../boards/index.js";
4
4
  //#region src/supervisor/health.ts
5
5
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"write-tools.d.ts","sourceRoot":"","sources":["../../src/supervisor/write-tools.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,gCAAgC,CAAC;AAE/E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AACrE,OAAO,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AAC3F,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEvD,UAAU,2BAA2B;IACnC,KAAK,EAAE,eAAe,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,gBAAgB,CAAC;IAC5B,KAAK,EAAE,YAAY,CAAC;IACpB,iBAAiB,EAAE,wBAAwB,CAAC;IAC5C,yBAAyB,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,gBAAgB,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,WAAW,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrH,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACpG,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAClB;AAED,wBAAgB,iCAAiC,CAAC,IAAI,EAAE,2BAA2B,GAAG,gBAAgB,CA4LrG"}
1
+ {"version":3,"file":"write-tools.d.ts","sourceRoot":"","sources":["../../src/supervisor/write-tools.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,gCAAgC,CAAC;AAE/E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AACrE,OAAO,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AAC3F,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEvD,UAAU,2BAA2B;IACnC,KAAK,EAAE,eAAe,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,gBAAgB,CAAC;IAC5B,KAAK,EAAE,YAAY,CAAC;IACpB,iBAAiB,EAAE,wBAAwB,CAAC;IAC5C,yBAAyB,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,gBAAgB,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,WAAW,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrH,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACpG,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAClB;AAED,wBAAgB,iCAAiC,CAAC,IAAI,EAAE,2BAA2B,GAAG,gBAAgB,CA+LrG"}
@@ -1,4 +1,6 @@
1
- import { FACTORY_RULE_STAGES, factoryRuleStage } from "../rules/types.js";
1
+ import { BOARD_IDENTIFIER_RE } from "../rules/validation.js";
2
+ import { boardForWorkItem } from "../boards/semantics.js";
3
+ import "../boards/index.js";
2
4
  import { createTool } from "@mastra/core/tools";
3
5
  import { z } from "zod";
4
6
  //#region src/supervisor/write-tools.ts
@@ -84,7 +86,7 @@ function createFactorySupervisorWriteTools(deps) {
84
86
  description: "Move or accept one Factory work item after the person confirms the destination stage.",
85
87
  inputSchema: z.object({
86
88
  workItemId: z.string().min(1),
87
- stage: z.enum(FACTORY_RULE_STAGES)
89
+ stage: z.string().max(128).regex(BOARD_IDENTIFIER_RE)
88
90
  }),
89
91
  requireApproval: true,
90
92
  execute: async ({ workItemId, stage }) => {
@@ -93,13 +95,13 @@ function createFactorySupervisorWriteTools(deps) {
93
95
  id: workItemId
94
96
  });
95
97
  if (!item || item.factoryProjectId !== deps.scope.factoryProjectId) throw new Error("Work item not found.");
96
- const from = factoryRuleStage(item.stages);
98
+ const from = item.stages.length === 1 ? item.stages[0] : void 0;
97
99
  if (!from) throw new Error("The work item does not have one valid Factory stage.");
98
100
  const result = await deps.transitionService.transition({
99
101
  orgId: deps.scope.orgId,
100
102
  factoryProjectId: deps.scope.factoryProjectId,
101
103
  workItemId,
102
- board: item.externalSource?.type === "pull-request" ? "review" : "work",
104
+ board: boardForWorkItem(item),
103
105
  stage,
104
106
  expectedRevision: item.revision,
105
107
  actor: {
@@ -1 +1 @@
1
- {"version":3,"file":"write-tools.js","names":[],"sources":["../../src/supervisor/write-tools.ts"],"sourcesContent":["import { createTool } from '@mastra/core/tools';\nimport { z } from 'zod';\n\nimport type { IntegrationTools } from '../integrations/base.js';\nimport type { FactoryTransitionService } from '../rules/transition-service.js';\nimport { FACTORY_RULE_STAGES, factoryRuleStage } from '../rules/types.js';\nimport type { AuditStorage } from '../storage/domains/audit/base.js';\nimport type { WorkItemRow, WorkItemsStorage } from '../storage/domains/work-items/base.js';\nimport type { SupervisorScope } from './read-tools.js';\n\ninterface SupervisorWriteDependencies {\n scope: SupervisorScope;\n userId: string;\n workItems: WorkItemsStorage;\n audit: AuditStorage;\n transitionService: FactoryTransitionService;\n reconcileAcceptanceLabels?: (input: { orgId: string; factoryProjectId: string; item: WorkItemRow }) => Promise<void>;\n signalSession?: (input: { sessionId: string; message: string; userId: string }) => Promise<unknown>;\n now?: () => Date;\n}\n\nexport function createFactorySupervisorWriteTools(deps: SupervisorWriteDependencies): IntegrationTools {\n const now = deps.now ?? (() => new Date());\n const audit = async (action: string, target: { type: string; id: string }, metadata: Record<string, unknown> = {}) =>\n deps.audit.record({\n orgId: deps.scope.orgId,\n actorId: deps.userId,\n actorType: 'human',\n action,\n targets: [target],\n factoryProjectId: deps.scope.factoryProjectId,\n metadata: { ...metadata, cause: 'supervisor' },\n occurredAt: now(),\n });\n\n return {\n factory_retry_decision: createTool({\n id: 'factory_retry_decision',\n description: 'Retry one failed Factory decision after the person confirms the repair.',\n inputSchema: z.object({ decisionId: z.string().min(1) }),\n requireApproval: true,\n execute: async ({ decisionId }) => {\n const decision = await deps.workItems.retryDeferredDecision(\n deps.scope.orgId,\n deps.scope.factoryProjectId,\n decisionId,\n now(),\n );\n if (!decision) throw new Error('The decision is not failed, no longer exists, or belongs to another factory.');\n await audit(\n 'factory.run.retry',\n { type: 'rule_decision', id: decisionId },\n { workItemId: decision.workItemId },\n );\n return { decisionId, status: decision.status, workItemId: decision.workItemId };\n },\n }),\n factory_dismiss_decision: createTool({\n id: 'factory_dismiss_decision',\n description: 'Dismiss one parked Factory proposal after the person confirms it is no longer wanted.',\n inputSchema: z.object({ decisionId: z.string().min(1) }),\n requireApproval: true,\n execute: async ({ decisionId }) => {\n const decision = await deps.workItems.dismissDeferredDecision(\n deps.scope.orgId,\n deps.scope.factoryProjectId,\n decisionId,\n now(),\n );\n if (!decision)\n throw new Error('The decision is not proposed, no longer exists, or belongs to another factory.');\n await audit(\n 'factory.run.dismissed',\n { type: 'rule_decision', id: decisionId },\n { workItemId: decision.workItemId },\n );\n return { decisionId, status: decision.status, workItemId: decision.workItemId };\n },\n }),\n factory_resolve_proposal: createTool({\n id: 'factory_resolve_proposal',\n description: 'Approve or dismiss one proposed Factory decision after the person confirms the choice.',\n inputSchema: z.object({ decisionId: z.string().min(1), resolution: z.enum(['approve', 'dismiss']) }),\n requireApproval: true,\n execute: async ({ decisionId, resolution }) => {\n const decision =\n resolution === 'approve'\n ? await deps.workItems.approveDeferredDecision(\n deps.scope.orgId,\n deps.scope.factoryProjectId,\n decisionId,\n now(),\n deps.userId,\n )\n : await deps.workItems.dismissDeferredDecision(\n deps.scope.orgId,\n deps.scope.factoryProjectId,\n decisionId,\n now(),\n );\n if (!decision) throw new Error('The proposal is no longer open or belongs to another factory.');\n await audit(\n resolution === 'approve' ? 'factory.run.approved' : 'factory.run.dismissed',\n {\n type: 'rule_decision',\n id: decisionId,\n },\n { workItemId: decision.workItemId },\n );\n return { decisionId, resolution, status: decision.status, workItemId: decision.workItemId };\n },\n }),\n factory_transition_work_item: createTool({\n id: 'factory_transition_work_item',\n description: 'Move or accept one Factory work item after the person confirms the destination stage.',\n inputSchema: z.object({ workItemId: z.string().min(1), stage: z.enum(FACTORY_RULE_STAGES) }),\n requireApproval: true,\n execute: async ({ workItemId, stage }) => {\n const item = await deps.workItems.get({ orgId: deps.scope.orgId, id: workItemId });\n if (!item || item.factoryProjectId !== deps.scope.factoryProjectId) throw new Error('Work item not found.');\n const from = factoryRuleStage(item.stages);\n if (!from) throw new Error('The work item does not have one valid Factory stage.');\n const result = await deps.transitionService.transition({\n orgId: deps.scope.orgId,\n factoryProjectId: deps.scope.factoryProjectId,\n workItemId,\n board: item.externalSource?.type === 'pull-request' ? 'review' : 'work',\n stage,\n expectedRevision: item.revision,\n actor: { type: 'human', id: deps.userId },\n ingress: { type: 'human', identity: `supervisor:${deps.userId}:${workItemId}:${item.revision}:${stage}` },\n cause: 'supervisor',\n });\n await audit(\n result.status === 'accepted' ? 'factory.work_item.stage_moved' : 'factory.work_item.transition_rejected',\n { type: 'work_item', id: workItemId },\n result.status === 'accepted'\n ? { from, to: result.stage, revision: result.revision, transitionId: result.transitionId }\n : { from, to: stage, code: result.code, reason: result.reason, transitionId: result.transitionId },\n );\n if (result.status !== 'accepted') {\n throw new Error(`The transition was rejected (${result.code}): ${result.reason}`);\n }\n return result;\n },\n }),\n factory_reconcile_labels: createTool({\n id: 'factory_reconcile_labels',\n description: 'Reconcile stale acceptance labels for one accepted work item after the person confirms the repair.',\n inputSchema: z.object({ workItemId: z.string().min(1) }),\n requireApproval: true,\n execute: async ({ workItemId }) => {\n if (!deps.reconcileAcceptanceLabels) throw new Error('Acceptance-label reconciliation is unavailable.');\n const item = await deps.workItems.get({ orgId: deps.scope.orgId, id: workItemId });\n if (!item || item.factoryProjectId !== deps.scope.factoryProjectId) throw new Error('Work item not found.');\n if (!item.acceptedAt) throw new Error('The work item has not been accepted.');\n await deps.reconcileAcceptanceLabels({ ...deps.scope, item });\n await audit('factory.work_item.labels_reconciled', { type: 'work_item', id: workItemId });\n return { workItemId, reconciled: true };\n },\n }),\n factory_revoke_binding: createTool({\n id: 'factory_revoke_binding',\n description: 'Revoke an orphaned or stale run binding after a health finding and person confirmation.',\n inputSchema: z.object({ bindingId: z.string().min(1) }),\n requireApproval: true,\n execute: async ({ bindingId }) => {\n const binding = await deps.workItems.revokeRunBinding({\n orgId: deps.scope.orgId,\n factoryProjectId: deps.scope.factoryProjectId,\n bindingId,\n revokedAt: now(),\n });\n if (!binding) throw new Error('The binding is not active, no longer exists, or belongs to another factory.');\n await audit(\n 'factory.intake.binding_updated',\n { type: 'run_binding', id: bindingId },\n {\n workItemId: binding.workItemId,\n role: binding.role,\n status: 'revoked',\n },\n );\n return { bindingId, status: binding.status, workItemId: binding.workItemId, role: binding.role };\n },\n }),\n factory_signal_session: createTool({\n id: 'factory_signal_session',\n description: 'Send bounded guidance to a worker session after the person confirms the exact message.',\n inputSchema: z.object({ sessionId: z.string().min(1), message: z.string().trim().min(1).max(2000) }),\n requireApproval: true,\n execute: async ({ sessionId, message }) => {\n if (!deps.signalSession) throw new Error('Worker session signaling is unavailable.');\n const bindings = await deps.workItems.listRunBindings(deps.scope.orgId, deps.scope.factoryProjectId);\n const binding = bindings.find(row => row.sessionId === sessionId);\n if (!binding) throw new Error('The session does not belong to this factory.');\n await deps.signalSession({ sessionId, message, userId: deps.userId });\n await audit(\n 'factory.agent.signaled',\n { type: 'factory_session', id: sessionId },\n {\n workItemId: binding.workItemId,\n role: binding.role,\n },\n );\n return { sessionId, delivered: true, workItemId: binding.workItemId, role: binding.role };\n },\n }),\n };\n}\n"],"mappings":";;;;AAqBA,SAAgB,kCAAkC,MAAqD;CACrG,MAAM,MAAM,KAAK,8BAAc,IAAI,KAAK;CACxC,MAAM,QAAQ,OAAO,QAAgB,QAAsC,WAAoC,CAAC,MAC9G,KAAK,MAAM,OAAO;EAChB,OAAO,KAAK,MAAM;EAClB,SAAS,KAAK;EACd,WAAW;EACX;EACA,SAAS,CAAC,MAAM;EAChB,kBAAkB,KAAK,MAAM;EAC7B,UAAU;GAAE,GAAG;GAAU,OAAO;EAAa;EAC7C,YAAY,IAAI;CAClB,CAAC;CAEH,OAAO;EACL,wBAAwB,WAAW;GACjC,IAAI;GACJ,aAAa;GACb,aAAa,EAAE,OAAO,EAAE,YAAY,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;GACvD,iBAAiB;GACjB,SAAS,OAAO,EAAE,iBAAiB;IACjC,MAAM,WAAW,MAAM,KAAK,UAAU,sBACpC,KAAK,MAAM,OACX,KAAK,MAAM,kBACX,YACA,IAAI,CACN;IACA,IAAI,CAAC,UAAU,MAAM,IAAI,MAAM,8EAA8E;IAC7G,MAAM,MACJ,qBACA;KAAE,MAAM;KAAiB,IAAI;IAAW,GACxC,EAAE,YAAY,SAAS,WAAW,CACpC;IACA,OAAO;KAAE;KAAY,QAAQ,SAAS;KAAQ,YAAY,SAAS;IAAW;GAChF;EACF,CAAC;EACD,0BAA0B,WAAW;GACnC,IAAI;GACJ,aAAa;GACb,aAAa,EAAE,OAAO,EAAE,YAAY,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;GACvD,iBAAiB;GACjB,SAAS,OAAO,EAAE,iBAAiB;IACjC,MAAM,WAAW,MAAM,KAAK,UAAU,wBACpC,KAAK,MAAM,OACX,KAAK,MAAM,kBACX,YACA,IAAI,CACN;IACA,IAAI,CAAC,UACH,MAAM,IAAI,MAAM,gFAAgF;IAClG,MAAM,MACJ,yBACA;KAAE,MAAM;KAAiB,IAAI;IAAW,GACxC,EAAE,YAAY,SAAS,WAAW,CACpC;IACA,OAAO;KAAE;KAAY,QAAQ,SAAS;KAAQ,YAAY,SAAS;IAAW;GAChF;EACF,CAAC;EACD,0BAA0B,WAAW;GACnC,IAAI;GACJ,aAAa;GACb,aAAa,EAAE,OAAO;IAAE,YAAY,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC;IAAG,YAAY,EAAE,KAAK,CAAC,WAAW,SAAS,CAAC;GAAE,CAAC;GACnG,iBAAiB;GACjB,SAAS,OAAO,EAAE,YAAY,iBAAiB;IAC7C,MAAM,WACJ,eAAe,YACX,MAAM,KAAK,UAAU,wBACnB,KAAK,MAAM,OACX,KAAK,MAAM,kBACX,YACA,IAAI,GACJ,KAAK,MACP,IACA,MAAM,KAAK,UAAU,wBACnB,KAAK,MAAM,OACX,KAAK,MAAM,kBACX,YACA,IAAI,CACN;IACN,IAAI,CAAC,UAAU,MAAM,IAAI,MAAM,+DAA+D;IAC9F,MAAM,MACJ,eAAe,YAAY,yBAAyB,yBACpD;KACE,MAAM;KACN,IAAI;IACN,GACA,EAAE,YAAY,SAAS,WAAW,CACpC;IACA,OAAO;KAAE;KAAY;KAAY,QAAQ,SAAS;KAAQ,YAAY,SAAS;IAAW;GAC5F;EACF,CAAC;EACD,8BAA8B,WAAW;GACvC,IAAI;GACJ,aAAa;GACb,aAAa,EAAE,OAAO;IAAE,YAAY,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC;IAAG,OAAO,EAAE,KAAK,mBAAmB;GAAE,CAAC;GAC3F,iBAAiB;GACjB,SAAS,OAAO,EAAE,YAAY,YAAY;IACxC,MAAM,OAAO,MAAM,KAAK,UAAU,IAAI;KAAE,OAAO,KAAK,MAAM;KAAO,IAAI;IAAW,CAAC;IACjF,IAAI,CAAC,QAAQ,KAAK,qBAAqB,KAAK,MAAM,kBAAkB,MAAM,IAAI,MAAM,sBAAsB;IAC1G,MAAM,OAAO,iBAAiB,KAAK,MAAM;IACzC,IAAI,CAAC,MAAM,MAAM,IAAI,MAAM,sDAAsD;IACjF,MAAM,SAAS,MAAM,KAAK,kBAAkB,WAAW;KACrD,OAAO,KAAK,MAAM;KAClB,kBAAkB,KAAK,MAAM;KAC7B;KACA,OAAO,KAAK,gBAAgB,SAAS,iBAAiB,WAAW;KACjE;KACA,kBAAkB,KAAK;KACvB,OAAO;MAAE,MAAM;MAAS,IAAI,KAAK;KAAO;KACxC,SAAS;MAAE,MAAM;MAAS,UAAU,cAAc,KAAK,OAAO,GAAG,WAAW,GAAG,KAAK,SAAS,GAAG;KAAQ;KACxG,OAAO;IACT,CAAC;IACD,MAAM,MACJ,OAAO,WAAW,aAAa,kCAAkC,yCACjE;KAAE,MAAM;KAAa,IAAI;IAAW,GACpC,OAAO,WAAW,aACd;KAAE;KAAM,IAAI,OAAO;KAAO,UAAU,OAAO;KAAU,cAAc,OAAO;IAAa,IACvF;KAAE;KAAM,IAAI;KAAO,MAAM,OAAO;KAAM,QAAQ,OAAO;KAAQ,cAAc,OAAO;IAAa,CACrG;IACA,IAAI,OAAO,WAAW,YACpB,MAAM,IAAI,MAAM,gCAAgC,OAAO,KAAK,KAAK,OAAO,QAAQ;IAElF,OAAO;GACT;EACF,CAAC;EACD,0BAA0B,WAAW;GACnC,IAAI;GACJ,aAAa;GACb,aAAa,EAAE,OAAO,EAAE,YAAY,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;GACvD,iBAAiB;GACjB,SAAS,OAAO,EAAE,iBAAiB;IACjC,IAAI,CAAC,KAAK,2BAA2B,MAAM,IAAI,MAAM,iDAAiD;IACtG,MAAM,OAAO,MAAM,KAAK,UAAU,IAAI;KAAE,OAAO,KAAK,MAAM;KAAO,IAAI;IAAW,CAAC;IACjF,IAAI,CAAC,QAAQ,KAAK,qBAAqB,KAAK,MAAM,kBAAkB,MAAM,IAAI,MAAM,sBAAsB;IAC1G,IAAI,CAAC,KAAK,YAAY,MAAM,IAAI,MAAM,sCAAsC;IAC5E,MAAM,KAAK,0BAA0B;KAAE,GAAG,KAAK;KAAO;IAAK,CAAC;IAC5D,MAAM,MAAM,uCAAuC;KAAE,MAAM;KAAa,IAAI;IAAW,CAAC;IACxF,OAAO;KAAE;KAAY,YAAY;IAAK;GACxC;EACF,CAAC;EACD,wBAAwB,WAAW;GACjC,IAAI;GACJ,aAAa;GACb,aAAa,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;GACtD,iBAAiB;GACjB,SAAS,OAAO,EAAE,gBAAgB;IAChC,MAAM,UAAU,MAAM,KAAK,UAAU,iBAAiB;KACpD,OAAO,KAAK,MAAM;KAClB,kBAAkB,KAAK,MAAM;KAC7B;KACA,WAAW,IAAI;IACjB,CAAC;IACD,IAAI,CAAC,SAAS,MAAM,IAAI,MAAM,6EAA6E;IAC3G,MAAM,MACJ,kCACA;KAAE,MAAM;KAAe,IAAI;IAAU,GACrC;KACE,YAAY,QAAQ;KACpB,MAAM,QAAQ;KACd,QAAQ;IACV,CACF;IACA,OAAO;KAAE;KAAW,QAAQ,QAAQ;KAAQ,YAAY,QAAQ;KAAY,MAAM,QAAQ;IAAK;GACjG;EACF,CAAC;EACD,wBAAwB,WAAW;GACjC,IAAI;GACJ,aAAa;GACb,aAAa,EAAE,OAAO;IAAE,WAAW,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC;IAAG,SAAS,EAAE,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAAI;GAAE,CAAC;GACnG,iBAAiB;GACjB,SAAS,OAAO,EAAE,WAAW,cAAc;IACzC,IAAI,CAAC,KAAK,eAAe,MAAM,IAAI,MAAM,0CAA0C;IAEnF,MAAM,WAAU,MADO,KAAK,UAAU,gBAAgB,KAAK,MAAM,OAAO,KAAK,MAAM,gBAAgB,EAAA,CAC1E,MAAK,QAAO,IAAI,cAAc,SAAS;IAChE,IAAI,CAAC,SAAS,MAAM,IAAI,MAAM,8CAA8C;IAC5E,MAAM,KAAK,cAAc;KAAE;KAAW;KAAS,QAAQ,KAAK;IAAO,CAAC;IACpE,MAAM,MACJ,0BACA;KAAE,MAAM;KAAmB,IAAI;IAAU,GACzC;KACE,YAAY,QAAQ;KACpB,MAAM,QAAQ;IAChB,CACF;IACA,OAAO;KAAE;KAAW,WAAW;KAAM,YAAY,QAAQ;KAAY,MAAM,QAAQ;IAAK;GAC1F;EACF,CAAC;CACH;AACF"}
1
+ {"version":3,"file":"write-tools.js","names":[],"sources":["../../src/supervisor/write-tools.ts"],"sourcesContent":["import { createTool } from '@mastra/core/tools';\nimport { z } from 'zod';\n\nimport { boardForWorkItem } from '../boards/index.js';\nimport type { IntegrationTools } from '../integrations/base.js';\nimport type { FactoryTransitionService } from '../rules/transition-service.js';\nimport { BOARD_IDENTIFIER_RE, MAX_BOARD_IDENTIFIER_LENGTH } from '../rules/validation.js';\nimport type { AuditStorage } from '../storage/domains/audit/base.js';\nimport type { WorkItemRow, WorkItemsStorage } from '../storage/domains/work-items/base.js';\nimport type { SupervisorScope } from './read-tools.js';\n\ninterface SupervisorWriteDependencies {\n scope: SupervisorScope;\n userId: string;\n workItems: WorkItemsStorage;\n audit: AuditStorage;\n transitionService: FactoryTransitionService;\n reconcileAcceptanceLabels?: (input: { orgId: string; factoryProjectId: string; item: WorkItemRow }) => Promise<void>;\n signalSession?: (input: { sessionId: string; message: string; userId: string }) => Promise<unknown>;\n now?: () => Date;\n}\n\nexport function createFactorySupervisorWriteTools(deps: SupervisorWriteDependencies): IntegrationTools {\n const now = deps.now ?? (() => new Date());\n const audit = async (action: string, target: { type: string; id: string }, metadata: Record<string, unknown> = {}) =>\n deps.audit.record({\n orgId: deps.scope.orgId,\n actorId: deps.userId,\n actorType: 'human',\n action,\n targets: [target],\n factoryProjectId: deps.scope.factoryProjectId,\n metadata: { ...metadata, cause: 'supervisor' },\n occurredAt: now(),\n });\n\n return {\n factory_retry_decision: createTool({\n id: 'factory_retry_decision',\n description: 'Retry one failed Factory decision after the person confirms the repair.',\n inputSchema: z.object({ decisionId: z.string().min(1) }),\n requireApproval: true,\n execute: async ({ decisionId }) => {\n const decision = await deps.workItems.retryDeferredDecision(\n deps.scope.orgId,\n deps.scope.factoryProjectId,\n decisionId,\n now(),\n );\n if (!decision) throw new Error('The decision is not failed, no longer exists, or belongs to another factory.');\n await audit(\n 'factory.run.retry',\n { type: 'rule_decision', id: decisionId },\n { workItemId: decision.workItemId },\n );\n return { decisionId, status: decision.status, workItemId: decision.workItemId };\n },\n }),\n factory_dismiss_decision: createTool({\n id: 'factory_dismiss_decision',\n description: 'Dismiss one parked Factory proposal after the person confirms it is no longer wanted.',\n inputSchema: z.object({ decisionId: z.string().min(1) }),\n requireApproval: true,\n execute: async ({ decisionId }) => {\n const decision = await deps.workItems.dismissDeferredDecision(\n deps.scope.orgId,\n deps.scope.factoryProjectId,\n decisionId,\n now(),\n );\n if (!decision)\n throw new Error('The decision is not proposed, no longer exists, or belongs to another factory.');\n await audit(\n 'factory.run.dismissed',\n { type: 'rule_decision', id: decisionId },\n { workItemId: decision.workItemId },\n );\n return { decisionId, status: decision.status, workItemId: decision.workItemId };\n },\n }),\n factory_resolve_proposal: createTool({\n id: 'factory_resolve_proposal',\n description: 'Approve or dismiss one proposed Factory decision after the person confirms the choice.',\n inputSchema: z.object({ decisionId: z.string().min(1), resolution: z.enum(['approve', 'dismiss']) }),\n requireApproval: true,\n execute: async ({ decisionId, resolution }) => {\n const decision =\n resolution === 'approve'\n ? await deps.workItems.approveDeferredDecision(\n deps.scope.orgId,\n deps.scope.factoryProjectId,\n decisionId,\n now(),\n deps.userId,\n )\n : await deps.workItems.dismissDeferredDecision(\n deps.scope.orgId,\n deps.scope.factoryProjectId,\n decisionId,\n now(),\n );\n if (!decision) throw new Error('The proposal is no longer open or belongs to another factory.');\n await audit(\n resolution === 'approve' ? 'factory.run.approved' : 'factory.run.dismissed',\n {\n type: 'rule_decision',\n id: decisionId,\n },\n { workItemId: decision.workItemId },\n );\n return { decisionId, resolution, status: decision.status, workItemId: decision.workItemId };\n },\n }),\n factory_transition_work_item: createTool({\n id: 'factory_transition_work_item',\n description: 'Move or accept one Factory work item after the person confirms the destination stage.',\n inputSchema: z.object({\n workItemId: z.string().min(1),\n stage: z.string().max(MAX_BOARD_IDENTIFIER_LENGTH).regex(BOARD_IDENTIFIER_RE),\n }),\n requireApproval: true,\n execute: async ({ workItemId, stage }) => {\n const item = await deps.workItems.get({ orgId: deps.scope.orgId, id: workItemId });\n if (!item || item.factoryProjectId !== deps.scope.factoryProjectId) throw new Error('Work item not found.');\n const from = item.stages.length === 1 ? item.stages[0] : undefined;\n if (!from) throw new Error('The work item does not have one valid Factory stage.');\n const result = await deps.transitionService.transition({\n orgId: deps.scope.orgId,\n factoryProjectId: deps.scope.factoryProjectId,\n workItemId,\n board: boardForWorkItem(item),\n stage,\n expectedRevision: item.revision,\n actor: { type: 'human', id: deps.userId },\n ingress: { type: 'human', identity: `supervisor:${deps.userId}:${workItemId}:${item.revision}:${stage}` },\n cause: 'supervisor',\n });\n await audit(\n result.status === 'accepted' ? 'factory.work_item.stage_moved' : 'factory.work_item.transition_rejected',\n { type: 'work_item', id: workItemId },\n result.status === 'accepted'\n ? { from, to: result.stage, revision: result.revision, transitionId: result.transitionId }\n : { from, to: stage, code: result.code, reason: result.reason, transitionId: result.transitionId },\n );\n if (result.status !== 'accepted') {\n throw new Error(`The transition was rejected (${result.code}): ${result.reason}`);\n }\n return result;\n },\n }),\n factory_reconcile_labels: createTool({\n id: 'factory_reconcile_labels',\n description: 'Reconcile stale acceptance labels for one accepted work item after the person confirms the repair.',\n inputSchema: z.object({ workItemId: z.string().min(1) }),\n requireApproval: true,\n execute: async ({ workItemId }) => {\n if (!deps.reconcileAcceptanceLabels) throw new Error('Acceptance-label reconciliation is unavailable.');\n const item = await deps.workItems.get({ orgId: deps.scope.orgId, id: workItemId });\n if (!item || item.factoryProjectId !== deps.scope.factoryProjectId) throw new Error('Work item not found.');\n if (!item.acceptedAt) throw new Error('The work item has not been accepted.');\n await deps.reconcileAcceptanceLabels({ ...deps.scope, item });\n await audit('factory.work_item.labels_reconciled', { type: 'work_item', id: workItemId });\n return { workItemId, reconciled: true };\n },\n }),\n factory_revoke_binding: createTool({\n id: 'factory_revoke_binding',\n description: 'Revoke an orphaned or stale run binding after a health finding and person confirmation.',\n inputSchema: z.object({ bindingId: z.string().min(1) }),\n requireApproval: true,\n execute: async ({ bindingId }) => {\n const binding = await deps.workItems.revokeRunBinding({\n orgId: deps.scope.orgId,\n factoryProjectId: deps.scope.factoryProjectId,\n bindingId,\n revokedAt: now(),\n });\n if (!binding) throw new Error('The binding is not active, no longer exists, or belongs to another factory.');\n await audit(\n 'factory.intake.binding_updated',\n { type: 'run_binding', id: bindingId },\n {\n workItemId: binding.workItemId,\n role: binding.role,\n status: 'revoked',\n },\n );\n return { bindingId, status: binding.status, workItemId: binding.workItemId, role: binding.role };\n },\n }),\n factory_signal_session: createTool({\n id: 'factory_signal_session',\n description: 'Send bounded guidance to a worker session after the person confirms the exact message.',\n inputSchema: z.object({ sessionId: z.string().min(1), message: z.string().trim().min(1).max(2000) }),\n requireApproval: true,\n execute: async ({ sessionId, message }) => {\n if (!deps.signalSession) throw new Error('Worker session signaling is unavailable.');\n const bindings = await deps.workItems.listRunBindings(deps.scope.orgId, deps.scope.factoryProjectId);\n const binding = bindings.find(row => row.sessionId === sessionId);\n if (!binding) throw new Error('The session does not belong to this factory.');\n await deps.signalSession({ sessionId, message, userId: deps.userId });\n await audit(\n 'factory.agent.signaled',\n { type: 'factory_session', id: sessionId },\n {\n workItemId: binding.workItemId,\n role: binding.role,\n },\n );\n return { sessionId, delivered: true, workItemId: binding.workItemId, role: binding.role };\n },\n }),\n };\n}\n"],"mappings":";;;;;;AAsBA,SAAgB,kCAAkC,MAAqD;CACrG,MAAM,MAAM,KAAK,8BAAc,IAAI,KAAK;CACxC,MAAM,QAAQ,OAAO,QAAgB,QAAsC,WAAoC,CAAC,MAC9G,KAAK,MAAM,OAAO;EAChB,OAAO,KAAK,MAAM;EAClB,SAAS,KAAK;EACd,WAAW;EACX;EACA,SAAS,CAAC,MAAM;EAChB,kBAAkB,KAAK,MAAM;EAC7B,UAAU;GAAE,GAAG;GAAU,OAAO;EAAa;EAC7C,YAAY,IAAI;CAClB,CAAC;CAEH,OAAO;EACL,wBAAwB,WAAW;GACjC,IAAI;GACJ,aAAa;GACb,aAAa,EAAE,OAAO,EAAE,YAAY,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;GACvD,iBAAiB;GACjB,SAAS,OAAO,EAAE,iBAAiB;IACjC,MAAM,WAAW,MAAM,KAAK,UAAU,sBACpC,KAAK,MAAM,OACX,KAAK,MAAM,kBACX,YACA,IAAI,CACN;IACA,IAAI,CAAC,UAAU,MAAM,IAAI,MAAM,8EAA8E;IAC7G,MAAM,MACJ,qBACA;KAAE,MAAM;KAAiB,IAAI;IAAW,GACxC,EAAE,YAAY,SAAS,WAAW,CACpC;IACA,OAAO;KAAE;KAAY,QAAQ,SAAS;KAAQ,YAAY,SAAS;IAAW;GAChF;EACF,CAAC;EACD,0BAA0B,WAAW;GACnC,IAAI;GACJ,aAAa;GACb,aAAa,EAAE,OAAO,EAAE,YAAY,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;GACvD,iBAAiB;GACjB,SAAS,OAAO,EAAE,iBAAiB;IACjC,MAAM,WAAW,MAAM,KAAK,UAAU,wBACpC,KAAK,MAAM,OACX,KAAK,MAAM,kBACX,YACA,IAAI,CACN;IACA,IAAI,CAAC,UACH,MAAM,IAAI,MAAM,gFAAgF;IAClG,MAAM,MACJ,yBACA;KAAE,MAAM;KAAiB,IAAI;IAAW,GACxC,EAAE,YAAY,SAAS,WAAW,CACpC;IACA,OAAO;KAAE;KAAY,QAAQ,SAAS;KAAQ,YAAY,SAAS;IAAW;GAChF;EACF,CAAC;EACD,0BAA0B,WAAW;GACnC,IAAI;GACJ,aAAa;GACb,aAAa,EAAE,OAAO;IAAE,YAAY,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC;IAAG,YAAY,EAAE,KAAK,CAAC,WAAW,SAAS,CAAC;GAAE,CAAC;GACnG,iBAAiB;GACjB,SAAS,OAAO,EAAE,YAAY,iBAAiB;IAC7C,MAAM,WACJ,eAAe,YACX,MAAM,KAAK,UAAU,wBACnB,KAAK,MAAM,OACX,KAAK,MAAM,kBACX,YACA,IAAI,GACJ,KAAK,MACP,IACA,MAAM,KAAK,UAAU,wBACnB,KAAK,MAAM,OACX,KAAK,MAAM,kBACX,YACA,IAAI,CACN;IACN,IAAI,CAAC,UAAU,MAAM,IAAI,MAAM,+DAA+D;IAC9F,MAAM,MACJ,eAAe,YAAY,yBAAyB,yBACpD;KACE,MAAM;KACN,IAAI;IACN,GACA,EAAE,YAAY,SAAS,WAAW,CACpC;IACA,OAAO;KAAE;KAAY;KAAY,QAAQ,SAAS;KAAQ,YAAY,SAAS;IAAW;GAC5F;EACF,CAAC;EACD,8BAA8B,WAAW;GACvC,IAAI;GACJ,aAAa;GACb,aAAa,EAAE,OAAO;IACpB,YAAY,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC;IAC5B,OAAO,EAAE,OAAO,CAAC,CAAC,IAAA,GAA+B,CAAC,CAAC,MAAM,mBAAmB;GAC9E,CAAC;GACD,iBAAiB;GACjB,SAAS,OAAO,EAAE,YAAY,YAAY;IACxC,MAAM,OAAO,MAAM,KAAK,UAAU,IAAI;KAAE,OAAO,KAAK,MAAM;KAAO,IAAI;IAAW,CAAC;IACjF,IAAI,CAAC,QAAQ,KAAK,qBAAqB,KAAK,MAAM,kBAAkB,MAAM,IAAI,MAAM,sBAAsB;IAC1G,MAAM,OAAO,KAAK,OAAO,WAAW,IAAI,KAAK,OAAO,KAAK,KAAA;IACzD,IAAI,CAAC,MAAM,MAAM,IAAI,MAAM,sDAAsD;IACjF,MAAM,SAAS,MAAM,KAAK,kBAAkB,WAAW;KACrD,OAAO,KAAK,MAAM;KAClB,kBAAkB,KAAK,MAAM;KAC7B;KACA,OAAO,iBAAiB,IAAI;KAC5B;KACA,kBAAkB,KAAK;KACvB,OAAO;MAAE,MAAM;MAAS,IAAI,KAAK;KAAO;KACxC,SAAS;MAAE,MAAM;MAAS,UAAU,cAAc,KAAK,OAAO,GAAG,WAAW,GAAG,KAAK,SAAS,GAAG;KAAQ;KACxG,OAAO;IACT,CAAC;IACD,MAAM,MACJ,OAAO,WAAW,aAAa,kCAAkC,yCACjE;KAAE,MAAM;KAAa,IAAI;IAAW,GACpC,OAAO,WAAW,aACd;KAAE;KAAM,IAAI,OAAO;KAAO,UAAU,OAAO;KAAU,cAAc,OAAO;IAAa,IACvF;KAAE;KAAM,IAAI;KAAO,MAAM,OAAO;KAAM,QAAQ,OAAO;KAAQ,cAAc,OAAO;IAAa,CACrG;IACA,IAAI,OAAO,WAAW,YACpB,MAAM,IAAI,MAAM,gCAAgC,OAAO,KAAK,KAAK,OAAO,QAAQ;IAElF,OAAO;GACT;EACF,CAAC;EACD,0BAA0B,WAAW;GACnC,IAAI;GACJ,aAAa;GACb,aAAa,EAAE,OAAO,EAAE,YAAY,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;GACvD,iBAAiB;GACjB,SAAS,OAAO,EAAE,iBAAiB;IACjC,IAAI,CAAC,KAAK,2BAA2B,MAAM,IAAI,MAAM,iDAAiD;IACtG,MAAM,OAAO,MAAM,KAAK,UAAU,IAAI;KAAE,OAAO,KAAK,MAAM;KAAO,IAAI;IAAW,CAAC;IACjF,IAAI,CAAC,QAAQ,KAAK,qBAAqB,KAAK,MAAM,kBAAkB,MAAM,IAAI,MAAM,sBAAsB;IAC1G,IAAI,CAAC,KAAK,YAAY,MAAM,IAAI,MAAM,sCAAsC;IAC5E,MAAM,KAAK,0BAA0B;KAAE,GAAG,KAAK;KAAO;IAAK,CAAC;IAC5D,MAAM,MAAM,uCAAuC;KAAE,MAAM;KAAa,IAAI;IAAW,CAAC;IACxF,OAAO;KAAE;KAAY,YAAY;IAAK;GACxC;EACF,CAAC;EACD,wBAAwB,WAAW;GACjC,IAAI;GACJ,aAAa;GACb,aAAa,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;GACtD,iBAAiB;GACjB,SAAS,OAAO,EAAE,gBAAgB;IAChC,MAAM,UAAU,MAAM,KAAK,UAAU,iBAAiB;KACpD,OAAO,KAAK,MAAM;KAClB,kBAAkB,KAAK,MAAM;KAC7B;KACA,WAAW,IAAI;IACjB,CAAC;IACD,IAAI,CAAC,SAAS,MAAM,IAAI,MAAM,6EAA6E;IAC3G,MAAM,MACJ,kCACA;KAAE,MAAM;KAAe,IAAI;IAAU,GACrC;KACE,YAAY,QAAQ;KACpB,MAAM,QAAQ;KACd,QAAQ;IACV,CACF;IACA,OAAO;KAAE;KAAW,QAAQ,QAAQ;KAAQ,YAAY,QAAQ;KAAY,MAAM,QAAQ;IAAK;GACjG;EACF,CAAC;EACD,wBAAwB,WAAW;GACjC,IAAI;GACJ,aAAa;GACb,aAAa,EAAE,OAAO;IAAE,WAAW,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC;IAAG,SAAS,EAAE,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAAI;GAAE,CAAC;GACnG,iBAAiB;GACjB,SAAS,OAAO,EAAE,WAAW,cAAc;IACzC,IAAI,CAAC,KAAK,eAAe,MAAM,IAAI,MAAM,0CAA0C;IAEnF,MAAM,WAAU,MADO,KAAK,UAAU,gBAAgB,KAAK,MAAM,OAAO,KAAK,MAAM,gBAAgB,EAAA,CAC1E,MAAK,QAAO,IAAI,cAAc,SAAS;IAChE,IAAI,CAAC,SAAS,MAAM,IAAI,MAAM,8CAA8C;IAC5E,MAAM,KAAK,cAAc;KAAE;KAAW;KAAS,QAAQ,KAAK;IAAO,CAAC;IACpE,MAAM,MACJ,0BACA;KAAE,MAAM;KAAmB,IAAI;IAAU,GACzC;KACE,YAAY,QAAQ;KACpB,MAAM,QAAQ;IAChB,CACF;IACA,OAAO;KAAE;KAAW,WAAW;KAAM,YAAY,QAAQ;KAAY,MAAM,QAAQ;IAAK;GAC1F;EACF,CAAC;CACH;AACF"}
@@ -0,0 +1,7 @@
1
+ /** Browser-safe vocabulary: never send URLs, route parameters, or input contents. */
2
+ export declare const FACTORY_WEB_PAGES: readonly ["onboarding", "work", "review", "overview", "attention", "activity", "rules", "audit", "knowledge", "settings", "slack_connection", "workspace", "thread", "supervisor", "new_session", "new_factory"];
3
+ export interface FactoryWebActivity {
4
+ activity: 'page_view' | 'interaction';
5
+ page: (typeof FACTORY_WEB_PAGES)[number];
6
+ }
7
+ //# sourceMappingURL=telemetry-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telemetry-types.d.ts","sourceRoot":"","sources":["../src/telemetry-types.ts"],"names":[],"mappings":"AAAA,qFAAqF;AACrF,eAAO,MAAM,iBAAiB,kNAiBpB,CAAC;AAEX,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,WAAW,GAAG,aAAa,CAAC;IACtC,IAAI,EAAE,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;CAC1C"}
@@ -0,0 +1,24 @@
1
+ //#region src/telemetry-types.ts
2
+ /** Browser-safe vocabulary: never send URLs, route parameters, or input contents. */
3
+ const FACTORY_WEB_PAGES = [
4
+ "onboarding",
5
+ "work",
6
+ "review",
7
+ "overview",
8
+ "attention",
9
+ "activity",
10
+ "rules",
11
+ "audit",
12
+ "knowledge",
13
+ "settings",
14
+ "slack_connection",
15
+ "workspace",
16
+ "thread",
17
+ "supervisor",
18
+ "new_session",
19
+ "new_factory"
20
+ ];
21
+ //#endregion
22
+ export { FACTORY_WEB_PAGES };
23
+
24
+ //# sourceMappingURL=telemetry-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telemetry-types.js","names":[],"sources":["../src/telemetry-types.ts"],"sourcesContent":["/** Browser-safe vocabulary: never send URLs, route parameters, or input contents. */\nexport const FACTORY_WEB_PAGES = [\n 'onboarding',\n 'work',\n 'review',\n 'overview',\n 'attention',\n 'activity',\n 'rules',\n 'audit',\n 'knowledge',\n 'settings',\n 'slack_connection',\n 'workspace',\n 'thread',\n 'supervisor',\n 'new_session',\n 'new_factory',\n] as const;\n\nexport interface FactoryWebActivity {\n activity: 'page_view' | 'interaction';\n page: (typeof FACTORY_WEB_PAGES)[number];\n}\n"],"mappings":";;AACA,MAAa,oBAAoB;CAC/B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF"}
@@ -0,0 +1,9 @@
1
+ import type { FactoryWebActivity } from './telemetry-types.js';
2
+ /** Start with the default platform auth provider; custom identity authorities are separate work. */
3
+ export declare function isFactoryTelemetryEnabled(providerName: string | undefined): boolean;
4
+ /** Identity is resolved by the server. This never aliases existing CLI or platform profiles. */
5
+ export declare function captureFactoryWebActivity(activity: FactoryWebActivity, actor: {
6
+ userId: string;
7
+ orgId?: string;
8
+ }): void;
9
+ //# sourceMappingURL=telemetry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telemetry.d.ts","sourceRoot":"","sources":["../src/telemetry.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAI/D,oGAAoG;AACpG,wBAAgB,yBAAyB,CAAC,YAAY,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAKnF;AAMD,gGAAgG;AAChG,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,kBAAkB,EAC5B,KAAK,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GACxC,IAAI,CA6BN"}
@@ -0,0 +1,47 @@
1
+ import { PostHog } from "posthog-node";
2
+ //#region src/telemetry.ts
3
+ let client;
4
+ /** Start with the default platform auth provider; custom identity authorities are separate work. */
5
+ function isFactoryTelemetryEnabled(providerName) {
6
+ return providerName === "mastra-studio" && ![
7
+ "1",
8
+ "true",
9
+ "yes"
10
+ ].includes(process.env.MASTRA_TELEMETRY_DISABLED?.trim().toLowerCase() ?? "");
11
+ }
12
+ function env(name) {
13
+ return process.env[name]?.trim() || void 0;
14
+ }
15
+ /** Identity is resolved by the server. This never aliases existing CLI or platform profiles. */
16
+ function captureFactoryWebActivity(activity, actor) {
17
+ if (!isFactoryTelemetryEnabled("mastra-studio")) return;
18
+ try {
19
+ const deploymentId = env("MASTRA_DEPLOYMENT_ID");
20
+ client ??= new PostHog("phc_SBLpZVAB6jmHOct9CABq3PF0Yn5FU3G2FgT4xUr2XrT", {
21
+ host: "https://us.posthog.com",
22
+ flushAt: 1,
23
+ flushInterval: 0,
24
+ maxQueueSize: 100,
25
+ disableGeoip: true
26
+ });
27
+ client.capture({
28
+ distinctId: `factory:platform:${actor.userId}`,
29
+ event: "factory_web_activity",
30
+ properties: {
31
+ schema_version: 1,
32
+ activity: activity.activity,
33
+ page: activity.page,
34
+ platform_user_id: actor.userId,
35
+ platform_org_id: actor.orgId,
36
+ platform_hosted: Boolean(deploymentId),
37
+ platform_project_id: env("MASTRA_PROJECT_ID"),
38
+ deployment_id: deploymentId,
39
+ platform_region: env("MASTRA_PLATFORM_REGION")
40
+ }
41
+ });
42
+ } catch {}
43
+ }
44
+ //#endregion
45
+ export { captureFactoryWebActivity, isFactoryTelemetryEnabled };
46
+
47
+ //# sourceMappingURL=telemetry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telemetry.js","names":[],"sources":["../src/telemetry.ts"],"sourcesContent":["import { PostHog } from 'posthog-node';\n\nimport type { FactoryWebActivity } from './telemetry-types.js';\n\nlet client: PostHog | undefined;\n\n/** Start with the default platform auth provider; custom identity authorities are separate work. */\nexport function isFactoryTelemetryEnabled(providerName: string | undefined): boolean {\n return (\n providerName === 'mastra-studio' &&\n !['1', 'true', 'yes'].includes(process.env.MASTRA_TELEMETRY_DISABLED?.trim().toLowerCase() ?? '')\n );\n}\n\nfunction env(name: string): string | undefined {\n return process.env[name]?.trim() || undefined;\n}\n\n/** Identity is resolved by the server. This never aliases existing CLI or platform profiles. */\nexport function captureFactoryWebActivity(\n activity: FactoryWebActivity,\n actor: { userId: string; orgId?: string },\n): void {\n if (!isFactoryTelemetryEnabled('mastra-studio')) return;\n try {\n const deploymentId = env('MASTRA_DEPLOYMENT_ID');\n client ??= new PostHog('phc_SBLpZVAB6jmHOct9CABq3PF0Yn5FU3G2FgT4xUr2XrT', {\n host: 'https://us.posthog.com',\n flushAt: 1,\n flushInterval: 0,\n maxQueueSize: 100,\n disableGeoip: true,\n });\n client.capture({\n distinctId: `factory:platform:${actor.userId}`,\n event: 'factory_web_activity',\n properties: {\n schema_version: 1,\n activity: activity.activity,\n page: activity.page,\n platform_user_id: actor.userId,\n platform_org_id: actor.orgId,\n platform_hosted: Boolean(deploymentId),\n platform_project_id: env('MASTRA_PROJECT_ID'),\n deployment_id: deploymentId,\n platform_region: env('MASTRA_PLATFORM_REGION'),\n },\n });\n } catch {\n // Analytics is best effort and must never interrupt Factory usage.\n }\n}\n"],"mappings":";;AAIA,IAAI;;AAGJ,SAAgB,0BAA0B,cAA2C;CACnF,OACE,iBAAiB,mBACjB,CAAC;EAAC;EAAK;EAAQ;CAAK,CAAC,CAAC,SAAS,QAAQ,IAAI,2BAA2B,KAAK,CAAC,CAAC,YAAY,KAAK,EAAE;AAEpG;AAEA,SAAS,IAAI,MAAkC;CAC7C,OAAO,QAAQ,IAAI,KAAK,EAAE,KAAK,KAAK,KAAA;AACtC;;AAGA,SAAgB,0BACd,UACA,OACM;CACN,IAAI,CAAC,0BAA0B,eAAe,GAAG;CACjD,IAAI;EACF,MAAM,eAAe,IAAI,sBAAsB;EAC/C,WAAW,IAAI,QAAQ,mDAAmD;GACxE,MAAM;GACN,SAAS;GACT,eAAe;GACf,cAAc;GACd,cAAc;EAChB,CAAC;EACD,OAAO,QAAQ;GACb,YAAY,oBAAoB,MAAM;GACtC,OAAO;GACP,YAAY;IACV,gBAAgB;IAChB,UAAU,SAAS;IACnB,MAAM,SAAS;IACf,kBAAkB,MAAM;IACxB,iBAAiB,MAAM;IACvB,iBAAiB,QAAQ,YAAY;IACrC,qBAAqB,IAAI,mBAAmB;IAC5C,eAAe;IACf,iBAAiB,IAAI,wBAAwB;GAC/C;EACF,CAAC;CACH,QAAQ,CAER;AACF"}
@@ -66,7 +66,19 @@ Every phase in `defineBoard()` requires `kind: 'resting' | 'working' | 'terminal
66
66
 
67
67
  Runtime reads the installed board's declarations for consent arming, the external-author guard, kickoff seating, run-start lanes, terminal cleanup, sweeps, and supervisor findings; nothing name-matches phases, and custom boards inherit nothing from Work. `initialPhase` must be resting. Unknown board or phase fails closed: consent is requested, nothing is cleaned up, no seat is started or revoked.
68
68
 
69
- To make a custom phase terminal or seat an agent in it, change its `kind`/`role` in the definition. Do not add phase-name checks to the transition service, dispatcher, sweeps, or supervisor. Decision and tool-input validation still accept only built-in board IDs and phase names, so custom-board handlers cannot emit `transition` decisions into custom phases yet.
69
+ To make a custom phase terminal or seat an agent in it, change its `kind`/`role` in the definition. Do not add phase-name checks to the transition service, dispatcher, sweeps, or supervisor. Bound transition tools accept custom phase identifiers and validate live membership and topology on the item's installed board.
70
+
71
+ ## Execute a custom board
72
+
73
+ Use the custom-board execution example in the Factory README as the configuration pattern. Board and phase identifiers are case-sensitive, 1–128 letters, digits, underscores, or hyphens, beginning with a letter or digit, without surrounding whitespace. Lifecycle, tool-result, and integration decisions may target installed custom phases. Validate membership on the target board, never against a union of installed phase names.
74
+
75
+ A `transition` decision stays on the item's assigned board. An `upsertLinkedWorkItem` decision may target another installed board; materialization enters that board's declared initial phase before the requested destination. Neither decision can reassign an existing card. Targets are checked before acceptance and before uncommitted deferred effects execute. Preserve committed replay and its original `configVersion` even if the installed configuration changes.
76
+
77
+ Working roles identify bindings on the shared Code Agent, not separately configured agents. There is no per-role agent registration option. Declare `invokeSkill` decisions with the working role and a board-owned prompt (or an available skill); do not invent an `agents` configuration. For automatic session preparation, verify the deployment's GitHub integration, project connection and linked repository, sandbox, organization model credentials, and project automatic-run setting. Enter a working phase through the public transition route as an authorized human when approval is required.
78
+
79
+ Verify the actual journey: initial entry, lifecycle kickoff, the declared role's phase signal and binding, a bound transition tool, role handoff, completed tool-result ingestion, deferred transition, and terminal cleanup. Polling and persisted-message ingestion resolve the single current phase through the installed board. Live binding, reassignment, revision, topology, approval, and external-author checks still apply. Custom roles named `triage` do not inherit Work classification requirements. A tool-result handler receives normalized `result.status` and `result.value`; it does not receive raw tool arguments. Check the result payload needed by the board's policy rather than treating every completed tool call as business success.
80
+
81
+ Keep the remaining limits explicit: `factory-ui` still uses built-in stages and roles, completion metrics still use Work's `done` phase, `held-waiting` is Work-specific, and built-in board replacement/customization is unsupported. Existing integration defaults do not automatically route events to a custom board.
70
82
 
71
83
  ## Change a built-in handler
72
84
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mastra/factory",
3
- "version": "0.13.0-alpha.16",
3
+ "version": "0.13.0-alpha.17",
4
4
  "description": "Mastra Software Factory module: the server core behind the Mastra Software Factory — storage domains, integrations, and surfaces for agent-powered software delivery",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -55,12 +55,13 @@
55
55
  "chat": "^4.34.0",
56
56
  "@octokit/rest": "^22.0.1",
57
57
  "hono": "^4.12.8",
58
+ "posthog-node": "^5.46.1",
58
59
  "zod": "^4.3.6",
60
+ "@mastra/core": "1.65.0-alpha.11",
61
+ "@mastra/code-sdk": "1.7.0-alpha.14",
59
62
  "@mastra/auth-studio": "1.3.5",
60
- "@mastra/auth-workos": "1.6.5",
61
63
  "@mastra/slack": "1.6.3",
62
- "@mastra/core": "1.65.0-alpha.10",
63
- "@mastra/code-sdk": "1.7.0-alpha.13"
64
+ "@mastra/auth-workos": "1.6.5"
64
65
  },
65
66
  "devDependencies": {
66
67
  "@types/node": "22.20.1",
@@ -70,8 +71,8 @@
70
71
  "typescript-eslint": "^8.57.0",
71
72
  "vitest": "4.1.10",
72
73
  "@internal/lint": "0.0.130",
73
- "@mastra/libsql": "1.22.4-alpha.0",
74
- "@mastra/pg": "1.23.0-alpha.5",
74
+ "@mastra/pg": "1.23.0-alpha.6",
75
+ "@mastra/libsql": "1.22.4-alpha.1",
75
76
  "@internal/types-builder": "0.0.105",
76
77
  "@internal/workspace": "0.0.2"
77
78
  },