@mastra/factory 0.8.0-alpha.13 → 0.8.0-alpha.14

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 (95) hide show
  1. package/CHANGELOG.md +209 -0
  2. package/dist/factory.d.ts.map +1 -1
  3. package/dist/factory.js +11 -1
  4. package/dist/factory.js.map +1 -1
  5. package/dist/integrations/github/app-identity.d.ts +43 -0
  6. package/dist/integrations/github/app-identity.d.ts.map +1 -0
  7. package/dist/integrations/github/app-identity.js +62 -0
  8. package/dist/integrations/github/app-identity.js.map +1 -0
  9. package/dist/integrations/github/rules.d.ts +8 -0
  10. package/dist/integrations/github/rules.d.ts.map +1 -1
  11. package/dist/integrations/github/rules.js +30 -8
  12. package/dist/integrations/github/rules.js.map +1 -1
  13. package/dist/integrations/github/sandbox.d.ts +8 -5
  14. package/dist/integrations/github/sandbox.d.ts.map +1 -1
  15. package/dist/integrations/github/sandbox.js +36 -8
  16. package/dist/integrations/github/sandbox.js.map +1 -1
  17. package/dist/integrations/github/webhook.d.ts +20 -0
  18. package/dist/integrations/github/webhook.d.ts.map +1 -1
  19. package/dist/integrations/github/webhook.js +32 -5
  20. package/dist/integrations/github/webhook.js.map +1 -1
  21. package/dist/integrations/linear/rules.d.ts.map +1 -1
  22. package/dist/integrations/linear/rules.js +2 -1
  23. package/dist/integrations/linear/rules.js.map +1 -1
  24. package/dist/integrations/platform/github/event-worker.d.ts +2 -0
  25. package/dist/integrations/platform/github/event-worker.d.ts.map +1 -1
  26. package/dist/integrations/platform/github/event-worker.js +29 -7
  27. package/dist/integrations/platform/github/event-worker.js.map +1 -1
  28. package/dist/integrations/platform/github/integration.d.ts.map +1 -1
  29. package/dist/integrations/platform/github/integration.js +7 -1
  30. package/dist/integrations/platform/github/integration.js.map +1 -1
  31. package/dist/routes/config.d.ts +4 -0
  32. package/dist/routes/config.d.ts.map +1 -1
  33. package/dist/routes/config.js +6 -0
  34. package/dist/routes/config.js.map +1 -1
  35. package/dist/routes/knowledge.d.ts +152 -0
  36. package/dist/routes/knowledge.d.ts.map +1 -0
  37. package/dist/routes/knowledge.js +442 -0
  38. package/dist/routes/knowledge.js.map +1 -0
  39. package/dist/routes/surface.d.ts +1 -0
  40. package/dist/routes/surface.d.ts.map +1 -1
  41. package/dist/routes/surface.js +7 -0
  42. package/dist/routes/surface.js.map +1 -1
  43. package/dist/routes/work-items.d.ts.map +1 -1
  44. package/dist/routes/work-items.js +3 -1
  45. package/dist/routes/work-items.js.map +1 -1
  46. package/dist/rules/binding-context.d.ts.map +1 -1
  47. package/dist/rules/binding-context.js +4 -1
  48. package/dist/rules/binding-context.js.map +1 -1
  49. package/dist/rules/defaults.d.ts.map +1 -1
  50. package/dist/rules/defaults.js +87 -4
  51. package/dist/rules/defaults.js.map +1 -1
  52. package/dist/rules/dispatcher.d.ts +6 -0
  53. package/dist/rules/dispatcher.d.ts.map +1 -1
  54. package/dist/rules/dispatcher.js +118 -16
  55. package/dist/rules/dispatcher.js.map +1 -1
  56. package/dist/rules/processor.d.ts.map +1 -1
  57. package/dist/rules/processor.js +20 -7
  58. package/dist/rules/processor.js.map +1 -1
  59. package/dist/rules/resolve.d.ts +1 -0
  60. package/dist/rules/resolve.d.ts.map +1 -1
  61. package/dist/rules/resolve.js +3 -2
  62. package/dist/rules/resolve.js.map +1 -1
  63. package/dist/rules/start-coordinator.d.ts +2 -0
  64. package/dist/rules/start-coordinator.d.ts.map +1 -1
  65. package/dist/rules/start-coordinator.js +3 -1
  66. package/dist/rules/start-coordinator.js.map +1 -1
  67. package/dist/rules/terminal-cleanup.d.ts +23 -0
  68. package/dist/rules/terminal-cleanup.d.ts.map +1 -0
  69. package/dist/rules/terminal-cleanup.js +43 -0
  70. package/dist/rules/terminal-cleanup.js.map +1 -0
  71. package/dist/rules/tools.d.ts.map +1 -1
  72. package/dist/rules/tools.js +24 -2
  73. package/dist/rules/tools.js.map +1 -1
  74. package/dist/rules/transition-service.d.ts +3 -0
  75. package/dist/rules/transition-service.d.ts.map +1 -1
  76. package/dist/rules/transition-service.js +10 -6
  77. package/dist/rules/transition-service.js.map +1 -1
  78. package/dist/rules/types.d.ts +32 -3
  79. package/dist/rules/types.d.ts.map +1 -1
  80. package/dist/rules/types.js +2 -0
  81. package/dist/rules/types.js.map +1 -1
  82. package/dist/rules/validation.d.ts.map +1 -1
  83. package/dist/rules/validation.js +8 -3
  84. package/dist/rules/validation.js.map +1 -1
  85. package/dist/skills/service.d.ts +9 -0
  86. package/dist/skills/service.d.ts.map +1 -1
  87. package/dist/skills/service.js +10 -1
  88. package/dist/skills/service.js.map +1 -1
  89. package/dist/storage/domains/work-items/base.d.ts +67 -1
  90. package/dist/storage/domains/work-items/base.d.ts.map +1 -1
  91. package/dist/storage/domains/work-items/base.js +140 -12
  92. package/dist/storage/domains/work-items/base.js.map +1 -1
  93. package/factory-skills/factory-rereview/SKILL.md +1 -1
  94. package/factory-skills/factory-review/SKILL.md +1 -1
  95. package/package.json +7 -7
@@ -32,6 +32,18 @@ function isAgentActor(by) {
32
32
  if (by === void 0) return false;
33
33
  return by.startsWith("agent:") || by === "factory-tool-result-rule";
34
34
  }
35
+ /**
36
+ * Stages in which a bound run can still act on its work item. Mirrors the
37
+ * non-terminal subset of `FACTORY_RULE_STAGES` (rules/types.ts); bindings for
38
+ * items outside these stages are dead weight in the reconcile walk.
39
+ */
40
+ const ACTIVE_RUN_BINDING_STAGES = /* @__PURE__ */ new Set([
41
+ "intake",
42
+ "triage",
43
+ "planning",
44
+ "execute",
45
+ "review"
46
+ ]);
35
47
  const WORK_ITEMS_SCHEMA = {
36
48
  name: "work_items",
37
49
  columns: {
@@ -58,6 +70,10 @@ const WORK_ITEMS_SCHEMA = {
58
70
  type: "json",
59
71
  nullable: true
60
72
  },
73
+ autonomy_armed_at: {
74
+ type: "timestamp",
75
+ nullable: true
76
+ },
61
77
  revision: {
62
78
  type: "integer",
63
79
  default: 1
@@ -101,6 +117,7 @@ function toWorkItem(row) {
101
117
  stageHistory: row.stage_history,
102
118
  sessions: row.sessions,
103
119
  metadata: row.metadata,
120
+ autonomyArmedAt: row.autonomy_armed_at ?? null,
104
121
  revision: row.revision,
105
122
  createdBy: row.created_by,
106
123
  createdAt: row.created_at,
@@ -116,6 +133,7 @@ function patchColumns(changes) {
116
133
  ...changes.stageHistory !== void 0 ? { stage_history: changes.stageHistory } : {},
117
134
  ...changes.sessions !== void 0 ? { sessions: changes.sessions } : {},
118
135
  ...changes.metadata !== void 0 ? { metadata: changes.metadata } : {},
136
+ ...changes.autonomyArmedAt !== void 0 && changes.autonomyArmedAt !== null ? { autonomy_armed_at: changes.autonomyArmedAt } : {},
119
137
  ...changes.revision !== void 0 ? { revision: changes.revision } : {},
120
138
  ...changes.updatedAt !== void 0 ? { updated_at: changes.updatedAt } : {}
121
139
  };
@@ -664,8 +682,10 @@ var WorkItemsStorage = class extends FactoryStorageDomain {
664
682
  reason = input.evaluation.reason;
665
683
  return null;
666
684
  }
667
- if (existing.stages.length === 1 && existing.stages[0] === input.destinationStage) return null;
685
+ const arm = input.armAutonomy === true && !existing.autonomyArmedAt;
686
+ if (existing.stages.length === 1 && existing.stages[0] === input.destinationStage) return arm ? patchColumns({ autonomyArmedAt: now }) : null;
668
687
  return patchColumns({
688
+ ...arm ? { autonomyArmedAt: now } : {},
669
689
  stages: [input.destinationStage],
670
690
  stageHistory: applyStageTransition(existing.stageHistory, existing.stages, [input.destinationStage], input.actorId, now),
671
691
  revision: existing.revision + 1,
@@ -946,18 +966,37 @@ var WorkItemsStorage = class extends FactoryStorageDomain {
946
966
  });
947
967
  return proposed && row ? toDeferredDecision(row) : null;
948
968
  }
949
- /** Release an approved effect back to the dispatcher; only `proposed` rows are approvable. */
969
+ /**
970
+ * Release an approved effect back to the dispatcher; only `proposed` rows are
971
+ * approvable. Approval is a person taking the item on, so the item's autonomy
972
+ * is armed in the same transaction — a crash cannot release the run while
973
+ * leaving its follow-up work parked for re-approval.
974
+ */
950
975
  async approveDeferredDecision(orgId, factoryProjectId, decisionId, now) {
951
- return this.#settleProposedDecision({
952
- orgId,
953
- factoryProjectId,
954
- decisionId
955
- }, {
956
- status: "pending",
957
- attempts: 0,
958
- available_at: now,
959
- approved_at: now,
960
- updated_at: now
976
+ return this.storage.withTransaction(async (ops) => {
977
+ let settled = false;
978
+ const row = await ops.updateAtomic("factory_deferred_decisions", {
979
+ id: decisionId,
980
+ org_id: orgId,
981
+ factory_project_id: factoryProjectId
982
+ }, (current) => {
983
+ if (current.status !== "proposed") return null;
984
+ settled = true;
985
+ return {
986
+ status: "pending",
987
+ attempts: 0,
988
+ available_at: now,
989
+ approved_at: now,
990
+ updated_at: now
991
+ };
992
+ });
993
+ if (!settled || !row) return null;
994
+ const record = toDeferredDecision(row);
995
+ if (record.workItemId) await ops.updateAtomic("work_items", {
996
+ org_id: orgId,
997
+ id: record.workItemId
998
+ }, (current) => current.autonomy_armed_at ? null : { autonomy_armed_at: now });
999
+ return record;
961
1000
  });
962
1001
  }
963
1002
  /** Retire a proposal nobody wants: `dismissed` is terminal, so the run never happens. */
@@ -972,6 +1011,29 @@ var WorkItemsStorage = class extends FactoryStorageDomain {
972
1011
  completed_at: now
973
1012
  });
974
1013
  }
1014
+ /**
1015
+ * Retire proposals parked on a work item. A merged pull request (or an item
1016
+ * closed out any other way) leaves its queued runs with nothing left to do,
1017
+ * and they would otherwise sit on the card forever asking to be answered.
1018
+ *
1019
+ * Pass `role` to retire only the proposals a run now starting has overtaken:
1020
+ * a parked "start triage" is moot the moment triage is actually running.
1021
+ */
1022
+ async dismissProposalsForWorkItem(input) {
1023
+ const rows = await this.#db.findMany("factory_deferred_decisions", {
1024
+ org_id: input.orgId,
1025
+ factory_project_id: input.factoryProjectId,
1026
+ work_item_id: input.workItemId,
1027
+ status: "proposed"
1028
+ });
1029
+ const dismissed = [];
1030
+ for (const row of rows) {
1031
+ if (input.role !== void 0 && toDeferredDecision(row).decision.role !== input.role) continue;
1032
+ const record = await this.dismissDeferredDecision(input.orgId, input.factoryProjectId, row.id, input.dismissedAt);
1033
+ if (record) dismissed.push(record);
1034
+ }
1035
+ return dismissed;
1036
+ }
975
1037
  async #settleProposedDecision({ orgId, factoryProjectId, decisionId }, patch) {
976
1038
  let settled = false;
977
1039
  const row = await this.#db.updateAtomic("factory_deferred_decisions", {
@@ -1070,6 +1132,56 @@ var WorkItemsStorage = class extends FactoryStorageDomain {
1070
1132
  });
1071
1133
  return revoked && row ? toBinding(row) : null;
1072
1134
  }
1135
+ /**
1136
+ * Revoke every active binding for one work item (all roles). Called from
1137
+ * terminal-stage cleanup so completed items stop paying the reconcile walk.
1138
+ * Returns the number of bindings revoked.
1139
+ */
1140
+ async revokeRunBindingsForWorkItem(input) {
1141
+ return this.#db.updateMany("factory_run_bindings", {
1142
+ org_id: input.orgId,
1143
+ factory_project_id: input.factoryProjectId,
1144
+ work_item_id: input.workItemId,
1145
+ status: "active"
1146
+ }, {
1147
+ status: "revoked",
1148
+ revoked_at: input.revokedAt
1149
+ });
1150
+ }
1151
+ /**
1152
+ * Revoke leaked/legacy active bindings: older than `olderThan`, or whose
1153
+ * work item is gone, malformed, or already terminal. Terminal-stage cleanup
1154
+ * handles bindings go-forward; this sweep drains anything that slipped past
1155
+ * it. Returns the number of bindings revoked.
1156
+ */
1157
+ async revokeStaleRunBindings(input) {
1158
+ const bindings = await this.listActiveRunBindings();
1159
+ const itemCache = /* @__PURE__ */ new Map();
1160
+ let revoked = 0;
1161
+ for (const binding of bindings) {
1162
+ let stale = binding.createdAt.getTime() < input.olderThan.getTime();
1163
+ if (!stale) {
1164
+ const key = `${binding.orgId}:${binding.workItemId}`;
1165
+ let item = itemCache.get(key);
1166
+ if (item === void 0) {
1167
+ item = await this.get({
1168
+ orgId: binding.orgId,
1169
+ id: binding.workItemId
1170
+ });
1171
+ itemCache.set(key, item);
1172
+ }
1173
+ stale = !item || item.stages.length !== 1 || !ACTIVE_RUN_BINDING_STAGES.has(item.stages[0]) || item.factoryProjectId !== binding.factoryProjectId;
1174
+ }
1175
+ if (!stale) continue;
1176
+ if (await this.revokeRunBinding({
1177
+ orgId: binding.orgId,
1178
+ factoryProjectId: binding.factoryProjectId,
1179
+ bindingId: binding.id,
1180
+ revokedAt: input.now
1181
+ })) revoked += 1;
1182
+ }
1183
+ return revoked;
1184
+ }
1073
1185
  /** Enumerate active bindings for the server-owned restart reconciler. */
1074
1186
  async listActiveRunBindings() {
1075
1187
  return (await this.#db.findMany("factory_run_bindings", { status: "active" })).map(toBinding);
@@ -1174,6 +1286,10 @@ var WorkItemsStorage = class extends FactoryStorageDomain {
1174
1286
  });
1175
1287
  item = toRow(row);
1176
1288
  }
1289
+ if (input.armAutonomy && !item.autonomyArmedAt) {
1290
+ const armedRow = await ops.updateAtomic("work_items", { id: item.id }, (current) => current.autonomy_armed_at ? null : { autonomy_armed_at: now });
1291
+ if (armedRow) item = toRow(armedRow);
1292
+ }
1177
1293
  await ops.updateMany("factory_run_bindings", {
1178
1294
  org_id: input.orgId,
1179
1295
  factory_project_id: input.factoryProjectId,
@@ -1372,6 +1488,18 @@ var WorkItemsStorage = class extends FactoryStorageDomain {
1372
1488
  return this.#withProjectRelationTransaction(orgId, candidate.factory_project_id, run);
1373
1489
  }
1374
1490
  /**
1491
+ * Record that a person committed this item to the Factory. Only the first
1492
+ * time counts: the timestamp marks when the item stopped needing permission,
1493
+ * so later runs must not push it forward. Bumps no revision, because arming
1494
+ * is not a change anyone is editing against.
1495
+ */
1496
+ async armAutonomy({ orgId, id, now }) {
1497
+ await this.#db.updateAtomic("work_items", {
1498
+ org_id: orgId,
1499
+ id
1500
+ }, (current) => current.autonomy_armed_at ? null : { autonomy_armed_at: now });
1501
+ }
1502
+ /**
1375
1503
  * Drop the governance rows that materialized a source key so a deleted work item
1376
1504
  * is not resurrected by the prior-ingress replay path on the next intake poll.
1377
1505
  */