@mastra/factory 0.18.0 → 0.19.0-alpha.1
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.
- package/README.md +13 -1
- package/dist/boards/registry.d.ts.map +1 -1
- package/dist/boards/registry.js +0 -2
- package/dist/boards/registry.js.map +1 -1
- package/dist/factory.d.ts.map +1 -1
- package/dist/factory.js +12 -2
- package/dist/factory.js.map +1 -1
- package/dist/integrations/github/default-rules.d.ts +10 -2
- package/dist/integrations/github/default-rules.d.ts.map +1 -1
- package/dist/integrations/github/default-rules.js +28 -4
- package/dist/integrations/github/default-rules.js.map +1 -1
- package/dist/integrations/github/integration.js +1 -1
- package/dist/integrations/github/reconcile-worker.d.ts.map +1 -1
- package/dist/integrations/github/reconcile-worker.js +1 -2
- package/dist/integrations/github/reconcile-worker.js.map +1 -1
- package/dist/integrations/github/routes.d.ts.map +1 -1
- package/dist/integrations/github/routes.js +2 -15
- package/dist/integrations/github/routes.js.map +1 -1
- package/dist/integrations/github/rules.d.ts +5 -0
- package/dist/integrations/github/rules.d.ts.map +1 -1
- package/dist/integrations/github/rules.js +17 -8
- package/dist/integrations/github/rules.js.map +1 -1
- package/dist/integrations/github/stale-reviews.d.ts +22 -0
- package/dist/integrations/github/stale-reviews.d.ts.map +1 -0
- package/dist/integrations/github/stale-reviews.js +70 -0
- package/dist/integrations/github/stale-reviews.js.map +1 -0
- package/dist/integrations/gitlab/version-control.d.ts.map +1 -1
- package/dist/integrations/gitlab/version-control.js +4 -5
- package/dist/integrations/gitlab/version-control.js.map +1 -1
- package/dist/integrations/gitlab/webhook.d.ts.map +1 -1
- package/dist/integrations/gitlab/webhook.js +2 -2
- package/dist/integrations/gitlab/webhook.js.map +1 -1
- package/dist/integrations/issue-reconcile-worker.d.ts.map +1 -1
- package/dist/integrations/issue-reconcile-worker.js +1 -2
- package/dist/integrations/issue-reconcile-worker.js.map +1 -1
- package/dist/integrations/jira/integration.d.ts.map +1 -1
- package/dist/integrations/jira/integration.js +7 -1
- package/dist/integrations/jira/integration.js.map +1 -1
- package/dist/integrations/linear/integration.d.ts.map +1 -1
- package/dist/integrations/linear/integration.js +10 -30
- package/dist/integrations/linear/integration.js.map +1 -1
- package/dist/integrations/platform/github/event-worker.d.ts.map +1 -1
- package/dist/integrations/platform/github/event-worker.js +1 -2
- package/dist/integrations/platform/github/event-worker.js.map +1 -1
- package/dist/integrations/platform/github/integration.js +1 -1
- package/dist/integrations/platform/gitlab/event-worker.d.ts.map +1 -1
- package/dist/integrations/platform/gitlab/event-worker.js +1 -2
- package/dist/integrations/platform/gitlab/event-worker.js.map +1 -1
- package/dist/integrations/platform/jira/integration.d.ts.map +1 -1
- package/dist/integrations/platform/jira/integration.js +7 -1
- package/dist/integrations/platform/jira/integration.js.map +1 -1
- package/dist/integrations/platform/linear/event-worker.d.ts.map +1 -1
- package/dist/integrations/platform/linear/event-worker.js +1 -2
- package/dist/integrations/platform/linear/event-worker.js.map +1 -1
- package/dist/integrations/platform/linear/integration.d.ts.map +1 -1
- package/dist/integrations/platform/linear/integration.js +9 -9
- package/dist/integrations/platform/linear/integration.js.map +1 -1
- package/dist/integrations/slack/slack.d.ts.map +1 -1
- package/dist/integrations/slack/slack.js +1 -2
- package/dist/integrations/slack/slack.js.map +1 -1
- package/dist/review-verdict.d.ts +6 -0
- package/dist/review-verdict.d.ts.map +1 -0
- package/dist/review-verdict.js +14 -0
- package/dist/review-verdict.js.map +1 -0
- package/dist/routes/attention-providers.d.ts +6 -7
- package/dist/routes/attention-providers.d.ts.map +1 -1
- package/dist/routes/attention-providers.js +12 -5
- package/dist/routes/attention-providers.js.map +1 -1
- package/dist/routes/attention.d.ts +2 -0
- package/dist/routes/attention.d.ts.map +1 -1
- package/dist/routes/attention.js +9 -3
- package/dist/routes/attention.js.map +1 -1
- package/dist/routes/oauth.d.ts.map +1 -1
- package/dist/routes/oauth.js +3 -3
- package/dist/routes/oauth.js.map +1 -1
- package/dist/routes/source-control-sessions.d.ts.map +1 -1
- package/dist/routes/source-control-sessions.js +1 -2
- package/dist/routes/source-control-sessions.js.map +1 -1
- package/dist/routes/work-items.d.ts.map +1 -1
- package/dist/routes/work-items.js +10 -5
- package/dist/routes/work-items.js.map +1 -1
- package/dist/rules/decision-applicability.d.ts +26 -0
- package/dist/rules/decision-applicability.d.ts.map +1 -0
- package/dist/rules/decision-applicability.js +47 -0
- package/dist/rules/decision-applicability.js.map +1 -0
- package/dist/rules/dispatcher.d.ts +4 -0
- package/dist/rules/dispatcher.d.ts.map +1 -1
- package/dist/rules/dispatcher.js +33 -24
- package/dist/rules/dispatcher.js.map +1 -1
- package/dist/rules/processor.d.ts.map +1 -1
- package/dist/rules/processor.js +5 -5
- package/dist/rules/processor.js.map +1 -1
- package/dist/rules/transition-service.d.ts.map +1 -1
- package/dist/rules/transition-service.js +1 -2
- package/dist/rules/transition-service.js.map +1 -1
- package/dist/rules/types.d.ts +18 -1
- package/dist/rules/types.d.ts.map +1 -1
- package/dist/rules/types.js.map +1 -1
- package/dist/rules/validation.d.ts.map +1 -1
- package/dist/rules/validation.js +20 -0
- package/dist/rules/validation.js.map +1 -1
- package/dist/secret-encryption.js +2 -2
- package/dist/secret-encryption.js.map +1 -1
- package/dist/session/factory-session.d.ts.map +1 -1
- package/dist/session/factory-session.js +1 -2
- package/dist/session/factory-session.js.map +1 -1
- package/dist/session/source-control-tools.d.ts +9 -0
- package/dist/session/source-control-tools.d.ts.map +1 -1
- package/dist/session/source-control-tools.js +51 -2
- package/dist/session/source-control-tools.js.map +1 -1
- package/dist/state-signing.js +3 -3
- package/dist/state-signing.js.map +1 -1
- package/dist/storage/domains/audit/base.d.ts.map +1 -1
- package/dist/storage/domains/audit/base.js +2 -3
- package/dist/storage/domains/audit/base.js.map +1 -1
- package/dist/storage/domains/source-control/inmemory.d.ts.map +1 -1
- package/dist/storage/domains/source-control/inmemory.js +5 -6
- package/dist/storage/domains/source-control/inmemory.js.map +1 -1
- package/dist/storage/domains/work-items/base.d.ts +3 -0
- package/dist/storage/domains/work-items/base.d.ts.map +1 -1
- package/dist/storage/domains/work-items/base.js +15 -2
- package/dist/storage/domains/work-items/base.js.map +1 -1
- package/dist/supervisor/write-tools.d.ts +2 -0
- package/dist/supervisor/write-tools.d.ts.map +1 -1
- package/dist/supervisor/write-tools.js +3 -0
- package/dist/supervisor/write-tools.js.map +1 -1
- package/factory-skills/configure-factory-rules/SKILL.md +1 -1
- package/factory-skills/factory-gitlab-rereview/SKILL.md +1 -1
- package/factory-skills/factory-gitlab-review/SKILL.md +1 -1
- package/factory-skills/factory-rereview/SKILL.md +12 -3
- package/factory-skills/factory-review/SKILL.md +12 -3
- package/package.json +9 -9
package/dist/rules/dispatcher.js
CHANGED
|
@@ -8,10 +8,10 @@ import { moveCardToBoard } from "../boards/relocate.js";
|
|
|
8
8
|
import { FactoryDispatchError, factoryDispatchFailureCode, factoryDispatchFailureMetadata } from "./dispatch-errors.js";
|
|
9
9
|
import { isHumanActorId } from "../storage/domains/audit/actors.js";
|
|
10
10
|
import { resolvePromptInvocation, resolveSkillInvocation, resolveSkillResumeInvocation } from "../skills/service.js";
|
|
11
|
+
import { decisionOvertaken } from "./decision-applicability.js";
|
|
11
12
|
import { FACTORY_OPEN_RUN_STALE_MS, heartbeatSessionOpenRun, isOpenRunLiveElsewhere, listSessionOpenRuns, recordSessionRunStart, waitForSessionRunAudit } from "../session/run-audit.js";
|
|
12
13
|
import { withWorkItemFeed } from "../storage/domains/comments/feed-context.js";
|
|
13
14
|
import { RequestContext } from "@mastra/core/request-context";
|
|
14
|
-
import { randomUUID } from "crypto";
|
|
15
15
|
//#region src/rules/dispatcher.ts
|
|
16
16
|
const LEASE_MS = 3e4;
|
|
17
17
|
const POLL_MS = 1e3;
|
|
@@ -34,6 +34,8 @@ const BOOKKEEPING_MAX_IN_FLIGHT = 4;
|
|
|
34
34
|
const STALE_BINDING_SWEEP_INTERVAL_MS = 10 * 6e4;
|
|
35
35
|
const STALE_BINDING_TTL_MS = 1440 * 6e4;
|
|
36
36
|
const RECONCILE_INTERVAL_MS = 3e4;
|
|
37
|
+
/** The card moved on before this run could act; it settles as superseded, not succeeded. */
|
|
38
|
+
var FactoryDecisionSuperseded = class extends Error {};
|
|
37
39
|
function isTerminalFailure(attempts, failureCode) {
|
|
38
40
|
return attempts >= MAX_ATTEMPTS || !factoryDispatchFailureMetadata(failureCode).canRetry;
|
|
39
41
|
}
|
|
@@ -245,6 +247,7 @@ var FactoryDecisionDispatcher = class {
|
|
|
245
247
|
#refreshManagedMemorySettings;
|
|
246
248
|
#primeCredentials;
|
|
247
249
|
#feedReader;
|
|
250
|
+
#dismissStaleReviews;
|
|
248
251
|
#resolveLinkedWorkItemParentId;
|
|
249
252
|
#maxInFlight;
|
|
250
253
|
#staleBindingSweepIntervalMs;
|
|
@@ -269,7 +272,7 @@ var FactoryDecisionDispatcher = class {
|
|
|
269
272
|
this.#transitionService = options.transitionService;
|
|
270
273
|
this.#boards = options.boards ?? createBoardRegistry();
|
|
271
274
|
this.#storage = options.storage;
|
|
272
|
-
this.#ownerId = options.ownerId ?? `factory-dispatcher:${randomUUID()}`;
|
|
275
|
+
this.#ownerId = options.ownerId ?? `factory-dispatcher:${globalThis.crypto.randomUUID()}`;
|
|
273
276
|
this.#isAutoRunEnabled = options.isAutoRunEnabled;
|
|
274
277
|
this.#autoApprovePlans = options.autoApprovePlans;
|
|
275
278
|
this.#reconcileToolResults = options.reconcileToolResults;
|
|
@@ -277,6 +280,7 @@ var FactoryDecisionDispatcher = class {
|
|
|
277
280
|
this.#refreshManagedMemorySettings = options.refreshManagedMemorySettings;
|
|
278
281
|
this.#primeCredentials = options.primeCredentials;
|
|
279
282
|
this.#feedReader = options.feedReader;
|
|
283
|
+
this.#dismissStaleReviews = options.dismissStaleReviews;
|
|
280
284
|
this.#resolveLinkedWorkItemParentId = options.resolveLinkedWorkItemParentId;
|
|
281
285
|
const maxInFlight = options.maxInFlight ?? MAX_IN_FLIGHT;
|
|
282
286
|
this.#maxInFlight = Number.isFinite(maxInFlight) && maxInFlight > 0 ? Math.floor(maxInFlight) : MAX_IN_FLIGHT;
|
|
@@ -417,6 +421,10 @@ var FactoryDecisionDispatcher = class {
|
|
|
417
421
|
executionCompleted = true;
|
|
418
422
|
if (!await this.#storage.completeDeferredDecision(leaseIdentity(record, this.#ownerId), /* @__PURE__ */ new Date())) throw new Error("Factory decision lease was lost before completion.");
|
|
419
423
|
} catch (error) {
|
|
424
|
+
if (error instanceof FactoryDecisionSuperseded) {
|
|
425
|
+
await this.#storage.supersedeLeasedDecision(leaseIdentity(record, this.#ownerId), /* @__PURE__ */ new Date());
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
420
428
|
const failureCode = factoryDispatchFailureCode(error);
|
|
421
429
|
const terminal = isTerminalFailure(record.attempts, failureCode);
|
|
422
430
|
const failed = await this.#storage.failDeferredDecision({
|
|
@@ -565,7 +573,7 @@ var FactoryDecisionDispatcher = class {
|
|
|
565
573
|
await this.#upsertLinkedItem(record, decision, nextChain);
|
|
566
574
|
return;
|
|
567
575
|
case "invokeSkill": {
|
|
568
|
-
if (await this.#roleSuperseded(record, decision.role))
|
|
576
|
+
if (await this.#roleSuperseded(record, decision.role) || await this.#decisionOvertaken(record, decision.role)) throw new FactoryDecisionSuperseded();
|
|
569
577
|
const binding = await this.#requireOrPrepareBinding(record, decision.role);
|
|
570
578
|
await this.#withBindingSkillRun(binding.id, async () => {
|
|
571
579
|
const item = record.workItemId ? await this.#storage.get({
|
|
@@ -600,24 +608,7 @@ var FactoryDecisionDispatcher = class {
|
|
|
600
608
|
await this.#switchThread(session, binding);
|
|
601
609
|
const deliveryId = record.deliveryGeneration === 0 ? record.id : `${record.id}:retry:${record.deliveryGeneration}`;
|
|
602
610
|
const kickoffLanded = async () => (await session.thread.listActiveMessages()).some((message) => message.id === deliveryId);
|
|
603
|
-
const kickoffStale = async () =>
|
|
604
|
-
if (await this.#roleSuperseded(record, decision.role)) return true;
|
|
605
|
-
if ((await this.#findBinding(record, decision.role))?.id !== binding.id) return true;
|
|
606
|
-
if (!record.workItemId) return false;
|
|
607
|
-
const current = await this.#storage.get({
|
|
608
|
-
orgId: record.orgId,
|
|
609
|
-
id: record.workItemId
|
|
610
|
-
});
|
|
611
|
-
if (current === null) return true;
|
|
612
|
-
const decidedAt = record.createdAt.getTime();
|
|
613
|
-
const enteredSince = current.stageHistory.filter((entry) => entry.exitedAt === void 0 && new Date(entry.enteredAt).getTime() > decidedAt);
|
|
614
|
-
const board = boardForWorkItem(current);
|
|
615
|
-
const carriesRole = (stage) => resolvePhaseSemantics(this.#boards, board, stage)?.role === decision.role;
|
|
616
|
-
const movedOn = !current.stageHistory.some((entry) => new Date(entry.enteredAt).getTime() <= decidedAt && (entry.exitedAt === void 0 || new Date(entry.exitedAt).getTime() > decidedAt) && carriesRole(entry.stage)) ? enteredSince.filter((entry) => !carriesRole(entry.stage)) : enteredSince;
|
|
617
|
-
if (movedOn.length === 0) return false;
|
|
618
|
-
const siblingStages = new Set((await this.#storage.listDeferredDecisions(record.orgId, record.factoryProjectId)).filter((sibling) => sibling.evaluationId === record.evaluationId && sibling.decision.type === "transition").map((sibling) => sibling.decision.stage));
|
|
619
|
-
return movedOn.some((entry) => !siblingStages.has(entry.stage));
|
|
620
|
-
};
|
|
611
|
+
const kickoffStale = async () => await this.#roleSuperseded(record, decision.role) || (await this.#findBinding(record, decision.role))?.id !== binding.id || await this.#decisionOvertaken(record, decision.role);
|
|
621
612
|
const runStillActive = () => this.#controller.listActiveThreadRuns().some((active) => active.threadId === binding.threadId);
|
|
622
613
|
while (true) {
|
|
623
614
|
const openRun = watchRun(session, {
|
|
@@ -637,10 +628,10 @@ var FactoryDecisionDispatcher = class {
|
|
|
637
628
|
}
|
|
638
629
|
await waitForSessionRunAudit(session);
|
|
639
630
|
}
|
|
640
|
-
if (await this.#roleSuperseded(record, decision.role))
|
|
631
|
+
if (await this.#roleSuperseded(record, decision.role)) throw new FactoryDecisionSuperseded();
|
|
641
632
|
if ((await this.#requireBinding(record, decision.role)).id !== binding.id) throw new FactoryDispatchError("session_unavailable", `Factory binding ${binding.id} changed while waiting for its open run to end.`);
|
|
642
633
|
if (await kickoffLanded()) return;
|
|
643
|
-
if (await kickoffStale())
|
|
634
|
+
if (await kickoffStale()) throw new FactoryDecisionSuperseded();
|
|
644
635
|
const kickoffContents = await withWorkItemFeed(this.#feedReader, {
|
|
645
636
|
orgId: record.orgId,
|
|
646
637
|
factoryProjectId: record.factoryProjectId,
|
|
@@ -687,7 +678,7 @@ var FactoryDecisionDispatcher = class {
|
|
|
687
678
|
if (await kickoffLanded()) break;
|
|
688
679
|
if (!await run.wait()) throw new FactoryDispatchError("run_terminal_event_missing", "Factory skill invocation is waiting on a run whose terminal event was not observed.");
|
|
689
680
|
if (await kickoffLanded()) break;
|
|
690
|
-
if (await kickoffStale())
|
|
681
|
+
if (await kickoffStale()) throw new FactoryDecisionSuperseded();
|
|
691
682
|
run.arm();
|
|
692
683
|
settled = await sendKickoff();
|
|
693
684
|
}
|
|
@@ -737,6 +728,10 @@ var FactoryDecisionDispatcher = class {
|
|
|
737
728
|
}), true);
|
|
738
729
|
return;
|
|
739
730
|
}
|
|
731
|
+
case "dismissStaleReviews":
|
|
732
|
+
if (!this.#dismissStaleReviews) throw new Error("GitHub review dismissal is not configured.");
|
|
733
|
+
await this.#dismissStaleReviews(decision);
|
|
734
|
+
return;
|
|
740
735
|
case "notify": {
|
|
741
736
|
const binding = await this.#requireBinding(record);
|
|
742
737
|
const session = await this.#requireSession(binding);
|
|
@@ -944,6 +939,20 @@ var FactoryDecisionDispatcher = class {
|
|
|
944
939
|
* fresh decision for the role (the card came back to it) must still dispatch
|
|
945
940
|
* even though an older revoked binding for that role is on record.
|
|
946
941
|
*/
|
|
942
|
+
async #decisionOvertaken(record, role) {
|
|
943
|
+
if (!record.workItemId) return false;
|
|
944
|
+
const [item, siblings] = await Promise.all([this.#storage.get({
|
|
945
|
+
orgId: record.orgId,
|
|
946
|
+
id: record.workItemId
|
|
947
|
+
}), this.#storage.listDecisionsForEvaluations(record.orgId, record.factoryProjectId, [record.evaluationId])]);
|
|
948
|
+
return decisionOvertaken({
|
|
949
|
+
boards: this.#boards,
|
|
950
|
+
item,
|
|
951
|
+
record,
|
|
952
|
+
role,
|
|
953
|
+
siblings
|
|
954
|
+
});
|
|
955
|
+
}
|
|
947
956
|
async #roleSuperseded(record, role) {
|
|
948
957
|
if (!record.workItemId) return false;
|
|
949
958
|
const bindings = await this.#storage.listRunBindings(record.orgId, record.factoryProjectId, record.workItemId);
|