@mastra/factory 0.13.0-alpha.8 → 0.13.0
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 +232 -10
- package/dist/auth.d.ts +2 -0
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +29 -4
- package/dist/auth.js.map +1 -1
- package/dist/boards/define-board.d.ts +36 -2
- package/dist/boards/define-board.d.ts.map +1 -1
- package/dist/boards/define-board.js +58 -1
- package/dist/boards/define-board.js.map +1 -1
- package/dist/boards/index.d.ts +4 -2
- package/dist/boards/index.d.ts.map +1 -1
- package/dist/boards/index.js +2 -1
- package/dist/boards/index.js.map +1 -1
- package/dist/boards/relocate.d.ts +17 -0
- package/dist/boards/relocate.d.ts.map +1 -0
- package/dist/boards/relocate.js +53 -0
- package/dist/boards/relocate.js.map +1 -0
- package/dist/boards/review.d.ts +1 -1
- package/dist/boards/review.d.ts.map +1 -1
- package/dist/boards/review.js +17 -4
- package/dist/boards/review.js.map +1 -1
- package/dist/boards/semantics.d.ts +28 -0
- package/dist/boards/semantics.d.ts.map +1 -0
- package/dist/boards/semantics.js +41 -0
- package/dist/boards/semantics.js.map +1 -0
- package/dist/boards/work-tool-rules.d.ts +8 -0
- package/dist/boards/work-tool-rules.d.ts.map +1 -0
- package/dist/boards/work-tool-rules.js +20 -0
- package/dist/boards/work-tool-rules.js.map +1 -0
- package/dist/boards/work.d.ts +1 -1
- package/dist/boards/work.d.ts.map +1 -1
- package/dist/boards/work.js +13 -0
- package/dist/boards/work.js.map +1 -1
- package/dist/capabilities/intake.d.ts +2 -0
- package/dist/capabilities/intake.d.ts.map +1 -1
- package/dist/factory.d.ts +5 -6
- package/dist/factory.d.ts.map +1 -1
- package/dist/factory.js +41 -12
- package/dist/factory.js.map +1 -1
- package/dist/integrations/base.d.ts +7 -4
- package/dist/integrations/base.d.ts.map +1 -1
- package/dist/integrations/github/default-rules.d.ts +2 -2
- package/dist/integrations/github/default-rules.d.ts.map +1 -1
- package/dist/integrations/github/default-rules.js +2 -2
- package/dist/integrations/github/default-rules.js.map +1 -1
- package/dist/integrations/github/issue-reconciler.d.ts.map +1 -1
- package/dist/integrations/github/issue-reconciler.js +4 -3
- package/dist/integrations/github/issue-reconciler.js.map +1 -1
- package/dist/integrations/github/rules.d.ts +6 -2
- package/dist/integrations/github/rules.d.ts.map +1 -1
- package/dist/integrations/github/rules.js +96 -15
- package/dist/integrations/github/rules.js.map +1 -1
- package/dist/integrations/github/sandbox.d.ts +6 -3
- package/dist/integrations/github/sandbox.d.ts.map +1 -1
- package/dist/integrations/github/sandbox.js +21 -2
- package/dist/integrations/github/sandbox.js.map +1 -1
- package/dist/integrations/issue-reconciler.d.ts +2 -0
- package/dist/integrations/issue-reconciler.d.ts.map +1 -1
- package/dist/integrations/issue-reconciler.js +1 -3
- package/dist/integrations/issue-reconciler.js.map +1 -1
- package/dist/integrations/linear/agent-tools.d.ts.map +1 -1
- package/dist/integrations/linear/agent-tools.js.map +1 -1
- package/dist/integrations/linear/default-rules.d.ts +2 -2
- package/dist/integrations/linear/default-rules.d.ts.map +1 -1
- package/dist/integrations/linear/default-rules.js +2 -2
- package/dist/integrations/linear/default-rules.js.map +1 -1
- package/dist/integrations/linear/integration.js +1 -0
- package/dist/integrations/linear/integration.js.map +1 -1
- package/dist/integrations/linear/issue-reconciler.d.ts.map +1 -1
- package/dist/integrations/linear/issue-reconciler.js +9 -4
- package/dist/integrations/linear/issue-reconciler.js.map +1 -1
- package/dist/integrations/linear/reconciliation-config.d.ts.map +1 -1
- package/dist/integrations/linear/reconciliation-config.js.map +1 -1
- package/dist/integrations/linear/routes.d.ts.map +1 -1
- package/dist/integrations/linear/routes.js +19 -26
- package/dist/integrations/linear/routes.js.map +1 -1
- package/dist/integrations/linear/rules.d.ts +7 -2
- package/dist/integrations/linear/rules.d.ts.map +1 -1
- package/dist/integrations/linear/rules.js +20 -8
- package/dist/integrations/linear/rules.js.map +1 -1
- package/dist/integrations/platform/api-client.d.ts.map +1 -1
- package/dist/integrations/platform/api-client.js +25 -4
- package/dist/integrations/platform/api-client.js.map +1 -1
- package/dist/integrations/platform/github/integration.d.ts +1 -0
- package/dist/integrations/platform/github/integration.d.ts.map +1 -1
- package/dist/integrations/platform/github/integration.js +5 -0
- package/dist/integrations/platform/github/integration.js.map +1 -1
- package/dist/integrations/platform/linear/integration.d.ts.map +1 -1
- package/dist/integrations/platform/linear/integration.js +5 -2
- package/dist/integrations/platform/linear/integration.js.map +1 -1
- package/dist/integrations/slack/emoji-shortcodes.generated.d.ts +8 -0
- package/dist/integrations/slack/emoji-shortcodes.generated.d.ts.map +1 -0
- package/dist/integrations/slack/emoji-shortcodes.generated.js +1985 -0
- package/dist/integrations/slack/emoji-shortcodes.generated.js.map +1 -0
- package/dist/integrations/slack/emoji.d.ts +3 -0
- package/dist/integrations/slack/emoji.d.ts.map +1 -0
- package/dist/integrations/slack/emoji.js +12 -0
- package/dist/integrations/slack/emoji.js.map +1 -0
- package/dist/integrations/slack/integration.js +1 -1
- package/dist/integrations/slack/integration.js.map +1 -1
- package/dist/integrations/slack/slack.d.ts.map +1 -1
- package/dist/integrations/slack/slack.js +16 -2
- package/dist/integrations/slack/slack.js.map +1 -1
- package/dist/routes/attention-activity.d.ts +2 -3
- package/dist/routes/attention-activity.d.ts.map +1 -1
- package/dist/routes/attention-activity.js +12 -3
- package/dist/routes/attention-activity.js.map +1 -1
- package/dist/routes/attention-parked.d.ts +22 -0
- package/dist/routes/attention-parked.d.ts.map +1 -0
- package/dist/routes/attention-parked.js +136 -0
- package/dist/routes/attention-parked.js.map +1 -0
- package/dist/routes/attention-providers.js +8 -8
- 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 +47 -118
- package/dist/routes/attention.js.map +1 -1
- package/dist/routes/contracts.d.ts +627 -0
- package/dist/routes/contracts.d.ts.map +1 -0
- package/dist/routes/contracts.js +468 -0
- package/dist/routes/contracts.js.map +1 -0
- package/dist/routes/intake.d.ts +18 -0
- package/dist/routes/intake.d.ts.map +1 -1
- package/dist/routes/intake.js +265 -15
- package/dist/routes/intake.js.map +1 -1
- package/dist/routes/projects.d.ts.map +1 -1
- package/dist/routes/projects.js +26 -76
- package/dist/routes/projects.js.map +1 -1
- package/dist/routes/supervisor.d.ts +2 -0
- package/dist/routes/supervisor.d.ts.map +1 -1
- package/dist/routes/supervisor.js +7 -6
- package/dist/routes/supervisor.js.map +1 -1
- package/dist/routes/surface.d.ts +7 -4
- package/dist/routes/surface.d.ts.map +1 -1
- package/dist/routes/surface.js +17 -11
- package/dist/routes/surface.js.map +1 -1
- package/dist/routes/telemetry.d.ts +13 -0
- package/dist/routes/telemetry.d.ts.map +1 -0
- package/dist/routes/telemetry.js +60 -0
- package/dist/routes/telemetry.js.map +1 -0
- package/dist/routes/work-items.d.ts +3 -3
- package/dist/routes/work-items.d.ts.map +1 -1
- package/dist/routes/work-items.js +134 -250
- package/dist/routes/work-items.js.map +1 -1
- package/dist/rules/dispatch-errors.d.ts +2 -2
- package/dist/rules/dispatch-errors.d.ts.map +1 -1
- package/dist/rules/dispatch-errors.js +10 -6
- package/dist/rules/dispatch-errors.js.map +1 -1
- package/dist/rules/dispatcher.d.ts +9 -2
- package/dist/rules/dispatcher.d.ts.map +1 -1
- package/dist/rules/dispatcher.js +76 -23
- package/dist/rules/dispatcher.js.map +1 -1
- package/dist/rules/index.d.ts +4 -5
- package/dist/rules/index.d.ts.map +1 -1
- package/dist/rules/index.js +3 -4
- package/dist/rules/processor.d.ts +3 -3
- package/dist/rules/processor.d.ts.map +1 -1
- package/dist/rules/processor.js +24 -31
- package/dist/rules/processor.js.map +1 -1
- package/dist/rules/resolve.d.ts +1 -3
- package/dist/rules/resolve.d.ts.map +1 -1
- package/dist/rules/resolve.js +1 -4
- package/dist/rules/resolve.js.map +1 -1
- package/dist/rules/start-coordinator.d.ts.map +1 -1
- package/dist/rules/start-coordinator.js +3 -1
- package/dist/rules/start-coordinator.js.map +1 -1
- package/dist/rules/tools.d.ts.map +1 -1
- package/dist/rules/tools.js +20 -10
- package/dist/rules/tools.js.map +1 -1
- package/dist/rules/transition-service.d.ts +7 -7
- package/dist/rules/transition-service.d.ts.map +1 -1
- package/dist/rules/transition-service.js +21 -25
- package/dist/rules/transition-service.js.map +1 -1
- package/dist/rules/types.d.ts +16 -11
- package/dist/rules/types.d.ts.map +1 -1
- package/dist/rules/types.js.map +1 -1
- package/dist/rules/validation.d.ts +12 -2
- package/dist/rules/validation.d.ts.map +1 -1
- package/dist/rules/validation.js +29 -20
- package/dist/rules/validation.js.map +1 -1
- package/dist/session/live-sessions.d.ts +33 -3
- package/dist/session/live-sessions.d.ts.map +1 -1
- package/dist/session/live-sessions.js +67 -5
- package/dist/session/live-sessions.js.map +1 -1
- package/dist/storage/domains/intake/base.d.ts +54 -1
- package/dist/storage/domains/intake/base.d.ts.map +1 -1
- package/dist/storage/domains/intake/base.js +136 -4
- package/dist/storage/domains/intake/base.js.map +1 -1
- package/dist/storage/domains/work-items/base.d.ts +22 -7
- package/dist/storage/domains/work-items/base.d.ts.map +1 -1
- package/dist/storage/domains/work-items/base.js +41 -26
- package/dist/storage/domains/work-items/base.js.map +1 -1
- package/dist/supervisor/health-worker.d.ts +2 -0
- package/dist/supervisor/health-worker.d.ts.map +1 -1
- package/dist/supervisor/health-worker.js +3 -1
- package/dist/supervisor/health-worker.js.map +1 -1
- package/dist/supervisor/health.d.ts +12 -14
- package/dist/supervisor/health.d.ts.map +1 -1
- package/dist/supervisor/health.js +24 -52
- package/dist/supervisor/health.js.map +1 -1
- package/dist/supervisor/instructions.d.ts +1 -1
- package/dist/supervisor/instructions.d.ts.map +1 -1
- package/dist/supervisor/instructions.js +10 -8
- package/dist/supervisor/instructions.js.map +1 -1
- package/dist/supervisor/read-tools.d.ts +2 -0
- package/dist/supervisor/read-tools.d.ts.map +1 -1
- package/dist/supervisor/read-tools.js +2 -2
- package/dist/supervisor/read-tools.js.map +1 -1
- package/dist/supervisor/write-tools.d.ts.map +1 -1
- package/dist/supervisor/write-tools.js +6 -4
- package/dist/supervisor/write-tools.js.map +1 -1
- package/dist/telemetry-types.d.ts +7 -0
- package/dist/telemetry-types.d.ts.map +1 -0
- package/dist/telemetry-types.js +24 -0
- package/dist/telemetry-types.js.map +1 -0
- package/dist/telemetry.d.ts +9 -0
- package/dist/telemetry.d.ts.map +1 -0
- package/dist/telemetry.js +47 -0
- package/dist/telemetry.js.map +1 -0
- package/dist/work-item-branch.d.ts +2 -0
- package/dist/work-item-branch.d.ts.map +1 -1
- package/dist/work-item-branch.js +6 -1
- package/dist/work-item-branch.js.map +1 -1
- package/dist/workspace.d.ts.map +1 -1
- package/dist/workspace.js +3 -1
- package/dist/workspace.js.map +1 -1
- package/factory-skills/configure-factory-rules/SKILL.md +30 -10
- package/package.json +11 -9
- package/dist/rules/defaults.d.ts +0 -9
- package/dist/rules/defaults.d.ts.map +0 -1
- package/dist/rules/defaults.js +0 -50
- package/dist/rules/defaults.js.map +0 -1
package/dist/rules/index.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export {
|
|
3
|
-
export type { ResolvedFactoryStageRule, ResolvedFactoryToolRule } from './resolve.js';
|
|
1
|
+
export { resolveFactoryStageRules } from './resolve.js';
|
|
2
|
+
export type { ResolvedFactoryStageRule } from './resolve.js';
|
|
4
3
|
export { FACTORY_GITHUB_EVENTS, FACTORY_LINEAR_EVENTS, FACTORY_RULE_BOARDS, FACTORY_RULE_SOURCES, FACTORY_RULE_STAGES, factoryRuleSourceForWorkItem, isFactoryRuleStage, } from './types.js';
|
|
5
|
-
export type { FactoryBoardRuleLeaf, FactoryBoundRuleContext, FactoryCommitDecision, FactoryGithubEventName, FactoryGithubRuleContext, FactoryLinearEventName, FactoryLinearRuleContext, FactoryInvokeSkillDecision, FactoryNotifyDecision, FactoryRuleActor, FactoryRuleBoard, FactoryRuleCausalEntry, FactoryRuleContextBase, FactoryRuleDecision, FactoryRuleHandler, FactoryRuleIngressIdentity, FactoryRuleItemContext, FactoryRuleJsonValue, FactoryRuleRejectionCode, FactoryRuleRejectDecision, FactoryRuleSource,
|
|
6
|
-
export {
|
|
4
|
+
export type { FactoryBoardRuleLeaf, FactoryBoundRuleContext, FactoryCommitDecision, FactoryGithubEventName, FactoryGithubRuleContext, FactoryLinearEventName, FactoryLinearRuleContext, FactoryInvokeSkillDecision, FactoryNotifyDecision, FactoryRuleActor, FactoryRuleBoard, FactoryRuleCausalEntry, FactoryRuleContextBase, FactoryRuleDecision, FactoryRuleHandler, FactoryRuleIngressIdentity, FactoryRuleItemContext, FactoryRuleJsonValue, FactoryRuleRejectionCode, FactoryRuleRejectDecision, FactoryRuleSource, FactoryRuleStage, FactorySendMessageDecision, FactoryStageRuleContext, FactoryToolResultRuleContext, FactoryTransitionDecision, FactoryTransitionResult, FactoryTransitionResultAccepted, FactoryTransitionResultRejected, FactoryUpsertLinkedWorkItemDecision, } from './types.js';
|
|
5
|
+
export { assertFactoryConfigVersion, DEFAULT_FACTORY_CONFIG_VERSION, FactoryRuleValidationError, MAX_FACTORY_RULE_CAUSAL_DEPTH, validateFactoryRuleDecision, validateFactoryRuleDecisions, } from './validation.js';
|
|
7
6
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/rules/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/rules/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AACxD,YAAY,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AAC7D,OAAO,EACL,qBAAqB,EACrB,qBAAqB,EACrB,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,EACnB,4BAA4B,EAC5B,kBAAkB,GACnB,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,oBAAoB,EACpB,uBAAuB,EACvB,qBAAqB,EACrB,sBAAsB,EACtB,wBAAwB,EACxB,sBAAsB,EACtB,wBAAwB,EACxB,0BAA0B,EAC1B,qBAAqB,EACrB,gBAAgB,EAChB,gBAAgB,EAChB,sBAAsB,EACtB,sBAAsB,EACtB,mBAAmB,EACnB,kBAAkB,EAClB,0BAA0B,EAC1B,sBAAsB,EACtB,oBAAoB,EACpB,wBAAwB,EACxB,yBAAyB,EACzB,iBAAiB,EACjB,gBAAgB,EAChB,0BAA0B,EAC1B,uBAAuB,EACvB,4BAA4B,EAC5B,yBAAyB,EACzB,uBAAuB,EACvB,+BAA+B,EAC/B,+BAA+B,EAC/B,mCAAmC,GACpC,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,0BAA0B,EAC1B,8BAA8B,EAC9B,0BAA0B,EAC1B,6BAA6B,EAC7B,2BAA2B,EAC3B,4BAA4B,GAC7B,MAAM,iBAAiB,CAAC"}
|
package/dist/rules/index.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
+
import { DEFAULT_FACTORY_CONFIG_VERSION, FactoryRuleValidationError, MAX_FACTORY_RULE_CAUSAL_DEPTH, assertFactoryConfigVersion, validateFactoryRuleDecision, validateFactoryRuleDecisions } from "./validation.js";
|
|
1
2
|
import { FACTORY_GITHUB_EVENTS, FACTORY_LINEAR_EVENTS, FACTORY_RULE_BOARDS, FACTORY_RULE_SOURCES, FACTORY_RULE_STAGES, factoryRuleSourceForWorkItem, isFactoryRuleStage } from "./types.js";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
import { DEFAULT_FACTORY_RULE_VERSION, builtInFactoryRules, defaultFactoryRules } from "./defaults.js";
|
|
5
|
-
export { DEFAULT_FACTORY_RULE_VERSION, FACTORY_GITHUB_EVENTS, FACTORY_LINEAR_EVENTS, FACTORY_RULE_BOARDS, FACTORY_RULE_SOURCES, FACTORY_RULE_STAGES, FactoryRuleValidationError, MAX_FACTORY_RULE_CAUSAL_DEPTH, assertFactoryRules, builtInFactoryRules, defaultFactoryRules, factoryRuleSourceForWorkItem, isFactoryRuleStage, resolveFactoryStageRules, resolveFactoryToolRule, validateFactoryRuleDecision, validateFactoryRuleDecisions };
|
|
3
|
+
import { resolveFactoryStageRules } from "./resolve.js";
|
|
4
|
+
export { DEFAULT_FACTORY_CONFIG_VERSION, FACTORY_GITHUB_EVENTS, FACTORY_LINEAR_EVENTS, FACTORY_RULE_BOARDS, FACTORY_RULE_SOURCES, FACTORY_RULE_STAGES, FactoryRuleValidationError, MAX_FACTORY_RULE_CAUSAL_DEPTH, assertFactoryConfigVersion, factoryRuleSourceForWorkItem, isFactoryRuleStage, resolveFactoryStageRules, validateFactoryRuleDecision, validateFactoryRuleDecisions };
|
|
@@ -3,7 +3,7 @@ import type { ComputeStateSignalArgs, ComputeStateSignalResult, ProcessInputStep
|
|
|
3
3
|
import type { BoardRegistry } from '../boards/index.js';
|
|
4
4
|
import type { FactoryRunBindingRecord, WorkItemsStorage, WorkItemRow } from '../storage/domains/work-items/base.js';
|
|
5
5
|
import type { FactoryTransitionService } from './transition-service.js';
|
|
6
|
-
import type { FactoryRuleJsonValue
|
|
6
|
+
import type { FactoryRuleJsonValue } from './types.js';
|
|
7
7
|
type PersistedMessageReader = {
|
|
8
8
|
listMessages(input: {
|
|
9
9
|
threadId: string;
|
|
@@ -29,9 +29,9 @@ export declare class FactoryPhaseStateProcessor implements Processor<'factory-ph
|
|
|
29
29
|
readonly id = "factory-phase";
|
|
30
30
|
readonly stateId = "factory-phase";
|
|
31
31
|
constructor(options: {
|
|
32
|
-
|
|
32
|
+
configVersion: string;
|
|
33
33
|
storage: WorkItemsStorage;
|
|
34
|
-
|
|
34
|
+
boards: BoardRegistry;
|
|
35
35
|
transitionService?: Pick<FactoryTransitionService, 'transition'>;
|
|
36
36
|
messageReader?: PersistedMessageReader;
|
|
37
37
|
recordPullRequestProvenance?: (input: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"processor.d.ts","sourceRoot":"","sources":["../../src/rules/processor.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAEpF,OAAO,KAAK,EACV,sBAAsB,EACtB,wBAAwB,EACxB,oBAAoB,EACpB,SAAS,EACV,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"processor.d.ts","sourceRoot":"","sources":["../../src/rules/processor.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAEpF,OAAO,KAAK,EACV,sBAAsB,EACtB,wBAAwB,EACxB,oBAAoB,EACpB,SAAS,EACV,MAAM,yBAAyB,CAAC;AAGjC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,uCAAuC,CAAC;AAEpH,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AAExE,OAAO,KAAK,EAGV,oBAAoB,EAGrB,MAAM,YAAY,CAAC;AAmBpB,KAAK,sBAAsB,GAAG;IAC5B,YAAY,CAAC,KAAK,EAAE;QAClB,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE;YAAE,SAAS,CAAC,EAAE;gBAAE,KAAK,CAAC,EAAE,IAAI,CAAA;aAAE,CAAA;SAAE,CAAC;QAC1C,OAAO,EAAE;YAAE,KAAK,EAAE,WAAW,CAAC;YAAC,SAAS,EAAE,KAAK,CAAA;SAAE,CAAC;KACnD,GAAG,OAAO,CAAC;QAAE,QAAQ,EAAE,eAAe,EAAE,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CAChE,CAAC;AAwKF,qBAAa,0BAA2B,YAAW,SAAS,CAAC,eAAe,CAAC;IAKzE,OAAO,CAAC,QAAQ,CAAC,OAAO;IAJ1B,QAAQ,CAAC,EAAE,mBAAY;IACvB,QAAQ,CAAC,OAAO,mBAAY;gBAGT,OAAO,EAAE;QACxB,aAAa,EAAE,MAAM,CAAC;QACtB,OAAO,EAAE,gBAAgB,CAAC;QAC1B,MAAM,EAAE,aAAa,CAAC;QACtB,iBAAiB,CAAC,EAAE,IAAI,CAAC,wBAAwB,EAAE,YAAY,CAAC,CAAC;QACjE,aAAa,CAAC,EAAE,sBAAsB,CAAC;QACvC,2BAA2B,CAAC,EAAE,CAAC,KAAK,EAAE;YACpC,OAAO,EAAE,uBAAuB,CAAC;YACjC,IAAI,EAAE,WAAW,CAAC;YAClB,kBAAkB,EAAE,MAAM,CAAC;YAC3B,UAAU,EAAE,MAAM,CAAC;YACnB,QAAQ,EAAE,MAAM,CAAC;YACjB,SAAS,EAAE,oBAAoB,CAAC;YAChC,UAAU,EAAE,oBAAoB,CAAC;YACjC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC;SAC7B,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;KACrB;IAGG,gBAAgB,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC;IAY9E,kBAAkB,CAAC,IAAI,EAAE,sBAAsB,GAAG,OAAO,CAAC,wBAAwB,CAAC;IAwFnF,wBAAwB,IAAI,OAAO,CAAC,IAAI,CAAC;IAMzC,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC;YAoCzD,cAAc;YA8Bd,gBAAgB;CAqG/B"}
|
package/dist/rules/processor.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { assertFactoryDecisionTarget, normalizeFactoryRuleJsonValue, validateFactoryRuleDecisions } from "./validation.js";
|
|
2
|
+
import { boardForWorkItem, resolveBoardToolRule, workItemPhaseSemantics } from "../boards/semantics.js";
|
|
3
|
+
import { workItemSource } from "./types.js";
|
|
4
|
+
import "../boards/index.js";
|
|
4
5
|
import { getFactorySessionCoordinates } from "./binding-context.js";
|
|
5
6
|
import { createHash } from "crypto";
|
|
6
7
|
import { resolveRequestThinkingLevel } from "@mastra/code-sdk/agents/model";
|
|
@@ -9,21 +10,12 @@ const STATE_ID = "factory-phase";
|
|
|
9
10
|
const RULE_TIMEOUT_MS = 5e3;
|
|
10
11
|
const TRANSCRIPT_PAGE_SIZE = 50;
|
|
11
12
|
const MAX_LINKED_ITEMS = 5;
|
|
12
|
-
function ruleStage(item) {
|
|
13
|
-
return item
|
|
13
|
+
function ruleStage(boards, item) {
|
|
14
|
+
return item && workItemPhaseSemantics(boards, item) ? item.stages[0] : void 0;
|
|
14
15
|
}
|
|
15
|
-
function itemInRuleStage(item) {
|
|
16
|
-
return ruleStage(item) !== void 0;
|
|
16
|
+
function itemInRuleStage(boards, item) {
|
|
17
|
+
return ruleStage(boards, item) !== void 0;
|
|
17
18
|
}
|
|
18
|
-
const PHASE_LABELS = {
|
|
19
|
-
intake: "Intake",
|
|
20
|
-
triage: "Investigating",
|
|
21
|
-
planning: "Planning",
|
|
22
|
-
execute: "Building",
|
|
23
|
-
review: "Reviewing",
|
|
24
|
-
done: "Done",
|
|
25
|
-
canceled: "Canceled"
|
|
26
|
-
};
|
|
27
19
|
function reviewRuntimeFromRequestContext(requestContext) {
|
|
28
20
|
if (!requestContext || typeof requestContext.get !== "function") throw new Error("Factory review phase requires a controller request context.");
|
|
29
21
|
const context = requestContext.get("controller");
|
|
@@ -38,9 +30,6 @@ function workItemSourceKey(item) {
|
|
|
38
30
|
const source = item.externalSource;
|
|
39
31
|
return source ? `${source.integrationId}:${source.type}:${source.externalId}` : null;
|
|
40
32
|
}
|
|
41
|
-
function boardForItem(item) {
|
|
42
|
-
return item.board ?? (item.externalSource?.type === "pull-request" ? "review" : "work");
|
|
43
|
-
}
|
|
44
33
|
function boundedError(value) {
|
|
45
34
|
return { message: (value instanceof Error ? value.message : typeof value === "string" ? value : "Tool execution failed.").slice(0, 2e3) };
|
|
46
35
|
}
|
|
@@ -175,13 +164,13 @@ var FactoryPhaseStateProcessor = class {
|
|
|
175
164
|
orgId: binding.orgId,
|
|
176
165
|
id: binding.workItemId
|
|
177
166
|
});
|
|
178
|
-
const stage = ruleStage(item);
|
|
167
|
+
const stage = ruleStage(this.options.boards, item);
|
|
179
168
|
if (!item || !stage) return;
|
|
180
169
|
const linked = (await this.options.storage.list({
|
|
181
170
|
orgId: binding.orgId,
|
|
182
171
|
factoryProjectId: binding.factoryProjectId
|
|
183
172
|
})).filter((candidate) => candidate.parentWorkItemId === item.id || item.parentWorkItemId === candidate.id).slice(0, MAX_LINKED_ITEMS);
|
|
184
|
-
const board =
|
|
173
|
+
const board = boardForWorkItem(item);
|
|
185
174
|
const baseValue = {
|
|
186
175
|
status: "active",
|
|
187
176
|
bindingId: binding.id,
|
|
@@ -189,7 +178,7 @@ var FactoryPhaseStateProcessor = class {
|
|
|
189
178
|
revision: item.revision,
|
|
190
179
|
stage,
|
|
191
180
|
role: binding.role,
|
|
192
|
-
|
|
181
|
+
configVersion: this.options.configVersion
|
|
193
182
|
};
|
|
194
183
|
const value = board === "review" ? {
|
|
195
184
|
...baseValue,
|
|
@@ -202,12 +191,12 @@ var FactoryPhaseStateProcessor = class {
|
|
|
202
191
|
const cacheKey = phaseCacheKey(value, linked);
|
|
203
192
|
if (hasBase && (args.tracking?.currentCacheKey ?? args.lastSnapshot?.metadata?.state?.cacheKey) === cacheKey) return;
|
|
204
193
|
const linkedText = linked.length ? `\nLinked items: ${linked.map((candidate) => `${workItemSource(candidate.externalSource)} ${candidate.title}`).join("; ")}` : "";
|
|
205
|
-
const phaseLabel = this.options.
|
|
194
|
+
const phaseLabel = this.options.boards.get(board)?.phases[stage]?.title ?? stage;
|
|
206
195
|
const runtime = value.modelId && value.thinkingLevel ? {
|
|
207
196
|
modelId: value.modelId,
|
|
208
197
|
thinkingLevel: value.thinkingLevel
|
|
209
198
|
} : null;
|
|
210
|
-
const snapshotContents = `Factory ${board} phase: ${escapeText(phaseLabel)} (${escapeText(stage)})\nWork item: ${escapeText(item.title)} (${item.id})\nRole: ${escapeText(binding.role)}\nRevision: ${item.revision}\
|
|
199
|
+
const snapshotContents = `Factory ${board} phase: ${escapeText(phaseLabel)} (${escapeText(stage)})\nWork item: ${escapeText(item.title)} (${item.id})\nRole: ${escapeText(binding.role)}\nRevision: ${item.revision}\nConfig: ${escapeText(this.options.configVersion)}\n` + (runtime ? `Runtime: model=${escapeText(runtime.modelId)}, reasoning-setting=${escapeText(runtime.thinkingLevel)}\n` : "") + ((board === "work" || board === "review") && stage === "intake" ? "This card rests in Intake: its work is paused. Answer questions without moving it; when the user asks to resume, request the transition into the working stage first, then continue the work in this session.\n" : "") + `Use factory_transition_work_item with expectedRevision ${item.revision} to request a phase change.${escapeText(linkedText)}`;
|
|
211
200
|
const isDelta = hasBase && prior?.status === "active";
|
|
212
201
|
return {
|
|
213
202
|
id: STATE_ID,
|
|
@@ -240,7 +229,7 @@ var FactoryPhaseStateProcessor = class {
|
|
|
240
229
|
orgId: binding.orgId,
|
|
241
230
|
id: binding.workItemId
|
|
242
231
|
});
|
|
243
|
-
if (!itemInRuleStage(item)) return;
|
|
232
|
+
if (!itemInRuleStage(this.options.boards, item)) return;
|
|
244
233
|
const cursor = await this.options.storage.getToolResultCursor(binding.orgId, binding.factoryProjectId, binding.id);
|
|
245
234
|
let page = 0;
|
|
246
235
|
while (true) {
|
|
@@ -274,7 +263,7 @@ var FactoryPhaseStateProcessor = class {
|
|
|
274
263
|
orgId: binding.orgId,
|
|
275
264
|
id: binding.workItemId
|
|
276
265
|
});
|
|
277
|
-
if (!itemInRuleStage(item)) return;
|
|
266
|
+
if (!itemInRuleStage(this.options.boards, item)) return;
|
|
278
267
|
for (const message of messages) for (const toolResult of completedToolResults(message)) {
|
|
279
268
|
if (toolCallIds && !toolCallIds.has(toolResult.toolCallId)) continue;
|
|
280
269
|
try {
|
|
@@ -293,7 +282,8 @@ var FactoryPhaseStateProcessor = class {
|
|
|
293
282
|
}
|
|
294
283
|
}
|
|
295
284
|
async ingestToolResult(binding, item, toolResult) {
|
|
296
|
-
const
|
|
285
|
+
const board = boardForWorkItem(item);
|
|
286
|
+
const rule = resolveBoardToolRule(this.options.boards, board, toolResult.toolName);
|
|
297
287
|
if (!rule) return;
|
|
298
288
|
const ingressId = JSON.stringify([
|
|
299
289
|
binding.id,
|
|
@@ -302,7 +292,6 @@ var FactoryPhaseStateProcessor = class {
|
|
|
302
292
|
toolResult.toolCallId
|
|
303
293
|
]);
|
|
304
294
|
if (await this.options.storage.getTransitionResultByIngress(binding.orgId, binding.factoryProjectId, ingressId)) return;
|
|
305
|
-
const board = boardForItem(item);
|
|
306
295
|
const context = {
|
|
307
296
|
tenant: {
|
|
308
297
|
orgId: binding.orgId,
|
|
@@ -319,7 +308,7 @@ var FactoryPhaseStateProcessor = class {
|
|
|
319
308
|
},
|
|
320
309
|
cause: `Completed ${toolResult.toolName}`,
|
|
321
310
|
causalChain: [],
|
|
322
|
-
|
|
311
|
+
configVersion: this.options.configVersion,
|
|
323
312
|
item: {
|
|
324
313
|
id: item.id,
|
|
325
314
|
source: workItemSource(item.externalSource),
|
|
@@ -352,7 +341,11 @@ var FactoryPhaseStateProcessor = class {
|
|
|
352
341
|
code: decision.code,
|
|
353
342
|
reason: decision.reason
|
|
354
343
|
};
|
|
355
|
-
else if (decision)
|
|
344
|
+
else if (decision) {
|
|
345
|
+
const validated = validateFactoryRuleDecisions([decision]);
|
|
346
|
+
for (const entry of validated) assertFactoryDecisionTarget(entry, this.options.boards, board);
|
|
347
|
+
decisions = validated;
|
|
348
|
+
}
|
|
356
349
|
} catch (error) {
|
|
357
350
|
const timedOut = error instanceof Error && error.message === "FACTORY_RULE_TIMEOUT";
|
|
358
351
|
outcome = {
|
|
@@ -369,7 +362,7 @@ var FactoryPhaseStateProcessor = class {
|
|
|
369
362
|
identity: ingressId,
|
|
370
363
|
triggerType: "tool.result"
|
|
371
364
|
},
|
|
372
|
-
|
|
365
|
+
configVersion: this.options.configVersion,
|
|
373
366
|
expectedRevision: item.revision,
|
|
374
367
|
actor: { ...context.actor },
|
|
375
368
|
outcome,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"processor.js","names":[],"sources":["../../src/rules/processor.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\n\nimport { resolveRequestThinkingLevel } from '@mastra/code-sdk/agents/model';\nimport type { ThinkingLevel } from '@mastra/code-sdk/providers/openai-codex';\nimport type { MastraCodeState } from '@mastra/code-sdk/schema';\nimport type { MastraDBMessage, MessageList } from '@mastra/core/agent/message-list';\nimport type { AgentControllerRequestContext } from '@mastra/core/agent-controller';\nimport type {\n ComputeStateSignalArgs,\n ComputeStateSignalResult,\n ProcessInputStepArgs,\n Processor,\n} from '@mastra/core/processors';\n\nimport type { BoardRegistry } from '../boards/index.js';\nimport type { FactoryRunBindingRecord, WorkItemsStorage, WorkItemRow } from '../storage/domains/work-items/base.js';\nimport { getFactorySessionCoordinates } from './binding-context.js';\nimport { resolveFactoryToolRule } from './resolve.js';\nimport type { FactoryTransitionService } from './transition-service.js';\nimport { factoryRuleStage, workItemSource } from './types.js';\nimport type {\n FactoryCommitDecision,\n FactoryRuleBoard,\n FactoryRuleDecision,\n FactoryRuleJsonValue,\n FactoryRuleStage,\n FactoryRules,\n FactoryToolResultRuleContext,\n} from './types.js';\nimport { normalizeFactoryRuleJsonValue, validateFactoryRuleDecisions } from './validation.js';\n\nconst STATE_ID = 'factory-phase';\nconst RULE_TIMEOUT_MS = 5_000;\nconst TRANSCRIPT_PAGE_SIZE = 50;\nconst MAX_LINKED_ITEMS = 5;\nfunction ruleStage(item: WorkItemRow | null | undefined): FactoryRuleStage | undefined {\n return item ? factoryRuleStage(item.stages) : undefined;\n}\n\nfunction itemInRuleStage(item: WorkItemRow | null | undefined): item is WorkItemRow {\n return ruleStage(item) !== undefined;\n}\n\nconst PHASE_LABELS: Record<FactoryRuleStage, string> = {\n intake: 'Intake',\n triage: 'Investigating',\n planning: 'Planning',\n execute: 'Building',\n review: 'Reviewing',\n done: 'Done',\n canceled: 'Canceled',\n};\n\ntype PersistedMessageReader = {\n listMessages(input: {\n threadId: string;\n resourceId?: string;\n page: number;\n perPage: number;\n filter?: { dateRange?: { start?: Date } };\n orderBy: { field: 'createdAt'; direction: 'ASC' };\n }): Promise<{ messages: MastraDBMessage[]; hasMore: boolean }>;\n};\n\ntype CompletedToolResult = {\n assistantMessageId: string;\n messageCreatedAt: Date;\n toolCallId: string;\n toolName: string;\n input: FactoryRuleJsonValue;\n status: 'success' | 'error';\n value: FactoryRuleJsonValue;\n};\n\ntype RuntimeSnapshot = {\n modelId: string;\n thinkingLevel: ThinkingLevel;\n};\n\ntype ActivePhaseSnapshotBase = {\n bindingId: string;\n itemId: string;\n revision: number;\n stage: FactoryRuleStage;\n role: string;\n ruleSetVersion: string;\n status: 'active';\n};\n\ntype ActivePhaseSnapshotValue = ActivePhaseSnapshotBase & {\n board: string;\n modelId?: string;\n thinkingLevel?: ThinkingLevel;\n};\n\ntype PhaseSnapshotValue = ActivePhaseSnapshotValue | { bindingId?: string; status: 'none' };\n\nfunction reviewRuntimeFromRequestContext(requestContext: ComputeStateSignalArgs['requestContext']): RuntimeSnapshot {\n if (!requestContext || typeof requestContext.get !== 'function') {\n throw new Error('Factory review phase requires a controller request context.');\n }\n const context = requestContext.get<'controller', AgentControllerRequestContext<MastraCodeState>>('controller');\n const modelId = context?.session?.modelId.trim();\n if (!modelId) {\n throw new Error('Factory review phase requires a selected session model.');\n }\n return { modelId, thinkingLevel: resolveRequestThinkingLevel(context) };\n}\n\nfunction workItemSourceKey(item: WorkItemRow): string | null {\n const source = item.externalSource;\n return source ? `${source.integrationId}:${source.type}:${source.externalId}` : null;\n}\n\nfunction boardForItem(item: WorkItemRow): FactoryRuleBoard {\n return item.board ?? (item.externalSource?.type === 'pull-request' ? 'review' : 'work');\n}\n\nfunction boundedError(value: unknown): FactoryRuleJsonValue {\n const message = value instanceof Error ? value.message : typeof value === 'string' ? value : 'Tool execution failed.';\n return { message: message.slice(0, 2_000) };\n}\n\nfunction boundedResult(value: unknown): FactoryRuleJsonValue {\n try {\n return normalizeFactoryRuleJsonValue(value);\n } catch {\n return { message: 'Tool result was not serializable.' };\n }\n}\n\nfunction messageParts(message: MastraDBMessage): unknown[] {\n const content = message.content as { parts?: unknown[]; toolInvocations?: unknown[] } | unknown[] | undefined;\n if (Array.isArray(content)) return content;\n if (Array.isArray(content?.parts)) return content.parts;\n return Array.isArray(content?.toolInvocations) ? content.toolInvocations : [];\n}\n\nfunction completedStepToolCallIds(steps: unknown[]): Set<string> {\n const ids = new Set<string>();\n for (const rawStep of steps) {\n if (!rawStep || typeof rawStep !== 'object') continue;\n const toolResults = (rawStep as { toolResults?: unknown[] }).toolResults;\n if (!Array.isArray(toolResults)) continue;\n for (const rawResult of toolResults) {\n if (!rawResult || typeof rawResult !== 'object') continue;\n const toolCallId = (rawResult as { toolCallId?: unknown }).toolCallId;\n if (typeof toolCallId === 'string') ids.add(toolCallId);\n }\n }\n return ids;\n}\n\nfunction completedToolResults(message: MastraDBMessage): CompletedToolResult[] {\n if (message.role !== 'assistant') return [];\n const createdAt = message.createdAt instanceof Date ? message.createdAt : new Date(message.createdAt);\n const completed: CompletedToolResult[] = [];\n for (const rawPart of messageParts(message)) {\n if (!rawPart || typeof rawPart !== 'object') continue;\n const part = rawPart as Record<string, unknown>;\n const invocation =\n part.type === 'tool-invocation' && part.toolInvocation && typeof part.toolInvocation === 'object'\n ? (part.toolInvocation as Record<string, unknown>)\n : part;\n const state = invocation.state;\n if (state !== 'result' && state !== 'error') continue;\n const toolCallId = invocation.toolCallId;\n const toolName = invocation.toolName ?? invocation.name;\n if (typeof toolCallId !== 'string' || typeof toolName !== 'string') continue;\n completed.push({\n assistantMessageId: message.id,\n messageCreatedAt: createdAt,\n toolCallId,\n toolName: toolName.slice(0, 256),\n input: boundedResult(invocation.args ?? {}),\n status: state === 'error' ? 'error' : 'success',\n value: state === 'error' ? boundedError(invocation.result ?? invocation.error) : boundedResult(invocation.result),\n });\n }\n return completed;\n}\n\nfunction currentCompletedToolMessage(\n messages: MastraDBMessage[],\n toolCallIds: ReadonlySet<string>,\n): MastraDBMessage | undefined {\n for (let index = messages.length - 1; index >= 0; index -= 1) {\n const message = messages[index]!;\n if (completedToolResults(message).some(result => toolCallIds.has(result.toolCallId))) return message;\n }\n return undefined;\n}\n\nfunction phaseCacheKey(value: ActivePhaseSnapshotValue, linked: WorkItemRow[]): string {\n return createHash('sha256')\n .update(\n JSON.stringify({\n ...value,\n linked: linked.map(item => [item.id, item.revision, item.stages[0]]),\n }),\n )\n .digest('hex');\n}\n\nfunction escapeText(value: string): string {\n return value.replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>');\n}\n\nfunction phaseFromSignal(signal: { metadata?: Record<string, unknown> } | undefined): PhaseSnapshotValue | undefined {\n return (signal?.metadata?.value as { phase?: PhaseSnapshotValue } | undefined)?.phase;\n}\n\nfunction latestPhase(args: ComputeStateSignalArgs): PhaseSnapshotValue | undefined {\n for (const signal of [...args.activeStateSignals].reverse()) {\n const phase = phaseFromSignal(signal);\n if (phase) return phase;\n }\n return phaseFromSignal(args.lastSnapshot);\n}\n\nasync function withRuleTimeout<T>(operation: Promise<T>): Promise<T> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n operation,\n new Promise<never>((_, reject) => {\n timer = setTimeout(() => reject(new Error('FACTORY_RULE_TIMEOUT')), RULE_TIMEOUT_MS);\n }),\n ]);\n } finally {\n if (timer) clearTimeout(timer);\n }\n}\n\nexport class FactoryPhaseStateProcessor implements Processor<'factory-phase'> {\n readonly id = STATE_ID;\n readonly stateId = STATE_ID;\n\n constructor(\n private readonly options: {\n rules: FactoryRules;\n storage: WorkItemsStorage;\n boardRegistry?: BoardRegistry;\n transitionService?: Pick<FactoryTransitionService, 'transition'>;\n messageReader?: PersistedMessageReader;\n recordPullRequestProvenance?: (input: {\n binding: FactoryRunBindingRecord;\n item: WorkItemRow;\n assistantMessageId: string;\n toolCallId: string;\n toolName: string;\n toolInput: FactoryRuleJsonValue;\n toolResult: FactoryRuleJsonValue;\n status: 'success' | 'error';\n }) => Promise<void>;\n },\n ) {}\n\n async processInputStep(args: ProcessInputStepArgs): Promise<MessageList | undefined> {\n const address = getFactorySessionCoordinates(args.requestContext);\n if (!address) return;\n const binding = await this.options.storage.findRunBindingBySession(address);\n if (!binding || binding.status !== 'active') return;\n const completedToolCallIds = completedStepToolCallIds(args.steps);\n const completedMessage = currentCompletedToolMessage(args.messages, completedToolCallIds);\n if (completedMessage) {\n await this.ingestMessages(binding, [completedMessage], completedToolCallIds);\n }\n }\n\n async computeStateSignal(args: ComputeStateSignalArgs): Promise<ComputeStateSignalResult> {\n const address = getFactorySessionCoordinates(args.requestContext);\n if (!address) return;\n const binding = await this.options.storage.findRunBindingBySession(address);\n const prior = latestPhase(args);\n const hasBase = Boolean(args.lastSnapshot) && args.contextWindow.hasSnapshot;\n\n if (!binding) return;\n if (binding.status !== 'active') {\n if (!hasBase || prior?.status !== 'active') return;\n return {\n id: STATE_ID,\n cacheKey: `factory:none:${prior.bindingId ?? 'revoked'}`,\n mode: 'snapshot',\n tagName: 'factory-phase',\n contents: '\\n',\n value: { phase: { status: 'none' } },\n attributes: { status: 'none' },\n metadata: { value: { phase: { status: 'none' } } },\n };\n }\n\n const item = await this.options.storage.get({ orgId: binding.orgId, id: binding.workItemId });\n const stage = ruleStage(item);\n if (!item || !stage) return;\n const allItems = await this.options.storage.list({\n orgId: binding.orgId,\n factoryProjectId: binding.factoryProjectId,\n });\n const linked = allItems\n .filter(candidate => candidate.parentWorkItemId === item.id || item.parentWorkItemId === candidate.id)\n .slice(0, MAX_LINKED_ITEMS);\n const board = boardForItem(item);\n const baseValue: ActivePhaseSnapshotBase = {\n status: 'active',\n bindingId: binding.id,\n itemId: item.id,\n revision: item.revision,\n stage,\n role: binding.role,\n ruleSetVersion: this.options.rules.version,\n };\n const value: ActivePhaseSnapshotValue =\n board === 'review'\n ? { ...baseValue, board, ...reviewRuntimeFromRequestContext(args.requestContext) }\n : { ...baseValue, board };\n const cacheKey = phaseCacheKey(value, linked);\n if (hasBase && (args.tracking?.currentCacheKey ?? args.lastSnapshot?.metadata?.state?.cacheKey) === cacheKey)\n return;\n\n const linkedText = linked.length\n ? `\\nLinked items: ${linked.map(candidate => `${workItemSource(candidate.externalSource)} ${candidate.title}`).join('; ')}`\n : '';\n const phaseLabel = this.options.boardRegistry?.get(board)?.phases[stage]?.title ?? PHASE_LABELS[stage] ?? stage;\n const runtime =\n value.modelId && value.thinkingLevel ? { modelId: value.modelId, thinkingLevel: value.thinkingLevel } : null;\n const snapshotContents =\n `Factory ${board} phase: ${escapeText(phaseLabel)} (${escapeText(stage)})\\n` +\n `Work item: ${escapeText(item.title)} (${item.id})\\n` +\n `Role: ${escapeText(binding.role)}\\nRevision: ${item.revision}\\nRules: ${escapeText(this.options.rules.version)}\\n` +\n (runtime\n ? `Runtime: model=${escapeText(runtime.modelId)}, reasoning-setting=${escapeText(runtime.thinkingLevel)}\\n`\n : '') +\n (stage === 'intake'\n ? 'This card rests in Intake: its work is paused. Answer questions without moving it; when the user asks to resume, request the transition into the working stage first, then continue the work in this session.\\n'\n : '') +\n `Use factory_transition_work_item with expectedRevision ${item.revision} to request a phase change.${escapeText(linkedText)}`;\n const isDelta = hasBase && prior?.status === 'active';\n return {\n id: STATE_ID,\n cacheKey,\n mode: isDelta ? 'delta' : 'snapshot',\n tagName: 'factory-phase',\n contents: isDelta ? `Factory phase update:\\n${snapshotContents}` : snapshotContents,\n value: { phase: value },\n ...(isDelta ? { delta: { phase: value } } : {}),\n attributes: {\n status: 'active',\n board,\n stage,\n role: binding.role,\n revision: item.revision,\n ...(runtime ?? {}),\n },\n metadata: { value: { phase: value } },\n };\n }\n\n async reconcileAllBoundThreads(): Promise<void> {\n if (!this.options.messageReader) return;\n const bindings = await this.options.storage.listActiveRunBindings();\n for (const binding of bindings) await this.reconcileBinding(binding);\n }\n\n async reconcileBinding(binding: FactoryRunBindingRecord): Promise<void> {\n const reader = this.options.messageReader;\n if (!reader || binding.status !== 'active') return;\n // One keyed read up front: a binding whose item is gone or no longer in a\n // single rule stage would ingest nothing, so skip the cursor and message\n // reads entirely instead of paying them on every walk.\n const item = await this.options.storage.get({ orgId: binding.orgId, id: binding.workItemId });\n if (!itemInRuleStage(item)) return;\n const cursor = await this.options.storage.getToolResultCursor(binding.orgId, binding.factoryProjectId, binding.id);\n let page = 0;\n while (true) {\n const result = await reader.listMessages({\n threadId: binding.threadId,\n resourceId: binding.resourceId,\n page,\n perPage: TRANSCRIPT_PAGE_SIZE,\n ...(cursor ? { filter: { dateRange: { start: cursor.lastMessageCreatedAt } } } : {}),\n orderBy: { field: 'createdAt', direction: 'ASC' },\n });\n await this.ingestMessages(binding, result.messages, undefined, item);\n const last = result.messages.at(-1);\n if (last) {\n await this.options.storage.advanceToolResultCursor({\n bindingId: binding.id,\n orgId: binding.orgId,\n factoryProjectId: binding.factoryProjectId,\n lastMessageId: last.id,\n lastMessageCreatedAt: last.createdAt instanceof Date ? last.createdAt : new Date(last.createdAt),\n updatedAt: new Date(),\n });\n }\n if (!result.hasMore) break;\n page += 1;\n }\n }\n\n private async ingestMessages(\n binding: FactoryRunBindingRecord,\n messages: MastraDBMessage[],\n toolCallIds?: ReadonlySet<string>,\n preloadedItem?: WorkItemRow,\n ): Promise<void> {\n const item = preloadedItem ?? (await this.options.storage.get({ orgId: binding.orgId, id: binding.workItemId }));\n if (!itemInRuleStage(item)) return;\n for (const message of messages) {\n for (const toolResult of completedToolResults(message)) {\n if (toolCallIds && !toolCallIds.has(toolResult.toolCallId)) continue;\n try {\n await this.options.recordPullRequestProvenance?.({\n binding,\n item,\n assistantMessageId: toolResult.assistantMessageId,\n toolCallId: toolResult.toolCallId,\n toolName: toolResult.toolName,\n toolInput: toolResult.input,\n toolResult: toolResult.value,\n status: toolResult.status,\n });\n } catch {\n // Provenance is supporting evidence and must not block authoritative rule ingress.\n }\n await this.ingestToolResult(binding, item, toolResult);\n }\n }\n }\n\n private async ingestToolResult(\n binding: FactoryRunBindingRecord,\n item: WorkItemRow,\n toolResult: CompletedToolResult,\n ): Promise<void> {\n const rule = resolveFactoryToolRule(this.options.rules, toolResult.toolName);\n if (!rule) return;\n const ingressId = JSON.stringify([\n binding.id,\n binding.threadId,\n toolResult.assistantMessageId,\n toolResult.toolCallId,\n ]);\n const prior = await this.options.storage.getTransitionResultByIngress(\n binding.orgId,\n binding.factoryProjectId,\n ingressId,\n );\n if (prior) return;\n const board = boardForItem(item);\n const context: FactoryToolResultRuleContext = {\n tenant: { orgId: binding.orgId, projectId: binding.factoryProjectId },\n actor: { type: 'agent', bindingId: binding.id, role: binding.role },\n ingress: { type: 'toolResult', id: ingressId },\n cause: `Completed ${toolResult.toolName}`,\n causalChain: [],\n ruleSetVersion: this.options.rules.version,\n item: {\n id: item.id,\n source: workItemSource(item.externalSource),\n sourceKey: workItemSourceKey(item),\n parentWorkItemId: item.parentWorkItemId,\n title: item.title,\n url: item.externalSource?.url ?? null,\n stages: item.stages,\n acceptedAt: item.acceptedAt,\n metadata: item.metadata,\n },\n board,\n itemRevision: item.revision,\n toolName: toolResult.toolName,\n threadId: binding.threadId,\n assistantMessageId: toolResult.assistantMessageId,\n toolCallId: toolResult.toolCallId,\n result: { status: toolResult.status, value: toolResult.value },\n };\n\n let decision: FactoryRuleDecision | void = undefined;\n let decisions: FactoryCommitDecision[] = [];\n let outcome: { status: 'accepted' | 'rejected'; code?: string; reason?: string } = { status: 'accepted' };\n try {\n decision = await withRuleTimeout(Promise.resolve(rule(Object.freeze(context))));\n if (decision?.type === 'reject') {\n outcome = { status: 'rejected', code: decision.code, reason: decision.reason };\n } else if (decision) {\n decisions = validateFactoryRuleDecisions([decision]);\n }\n } catch (error) {\n const timedOut = error instanceof Error && error.message === 'FACTORY_RULE_TIMEOUT';\n outcome = {\n status: 'rejected',\n code: timedOut ? 'timeout' : 'rule_error',\n reason: timedOut\n ? 'Factory rule evaluation timed out.'\n : error instanceof Error\n ? error.message.slice(0, 2_000)\n : 'Factory tool-result rule failed.',\n };\n }\n const committed = await this.options.storage.commitRuleEvaluation({\n orgId: binding.orgId,\n factoryProjectId: binding.factoryProjectId,\n workItemId: item.id,\n ingress: { identity: ingressId, triggerType: 'tool.result' },\n ruleSetVersion: this.options.rules.version,\n expectedRevision: item.revision,\n actor: { ...context.actor },\n outcome,\n decisions: decisions.map(entry => ({ ...entry })),\n causalChain: [],\n now: new Date(),\n });\n if (committed.status !== 'committed' || !this.options.transitionService) return;\n for (const entry of decisions) {\n if (entry.type !== 'transition') continue;\n await this.options.transitionService.transition({\n orgId: binding.orgId,\n factoryProjectId: binding.factoryProjectId,\n workItemId: item.id,\n board: entry.board,\n stage: entry.stage,\n expectedRevision: item.revision,\n actor: { type: 'system', id: 'factory-tool-result-rule' },\n ingress: { type: 'rule', identity: `decision:${entry.idempotencyKey}` },\n cause: 'tool_result_rule',\n causalChain: [{ ingressId, decisionType: entry.type }],\n });\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"processor.js","names":[],"sources":["../../src/rules/processor.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\n\nimport { resolveRequestThinkingLevel } from '@mastra/code-sdk/agents/model';\nimport type { ThinkingLevel } from '@mastra/code-sdk/providers/openai-codex';\nimport type { MastraCodeState } from '@mastra/code-sdk/schema';\nimport type { MastraDBMessage, MessageList } from '@mastra/core/agent/message-list';\nimport type { AgentControllerRequestContext } from '@mastra/core/agent-controller';\nimport type {\n ComputeStateSignalArgs,\n ComputeStateSignalResult,\n ProcessInputStepArgs,\n Processor,\n} from '@mastra/core/processors';\n\nimport { boardForWorkItem, resolveBoardToolRule, workItemPhaseSemantics } from '../boards/index.js';\nimport type { BoardRegistry } from '../boards/index.js';\nimport type { FactoryRunBindingRecord, WorkItemsStorage, WorkItemRow } from '../storage/domains/work-items/base.js';\nimport { getFactorySessionCoordinates } from './binding-context.js';\nimport type { FactoryTransitionService } from './transition-service.js';\nimport { workItemSource } from './types.js';\nimport type {\n FactoryCommitDecision,\n FactoryRuleDecision,\n FactoryRuleJsonValue,\n FactoryRuleStage,\n FactoryToolResultRuleContext,\n} from './types.js';\nimport {\n assertFactoryDecisionTarget,\n normalizeFactoryRuleJsonValue,\n validateFactoryRuleDecisions,\n} from './validation.js';\n\nconst STATE_ID = 'factory-phase';\nconst RULE_TIMEOUT_MS = 5_000;\nconst TRANSCRIPT_PAGE_SIZE = 50;\nconst MAX_LINKED_ITEMS = 5;\nfunction ruleStage(boards: BoardRegistry, item: WorkItemRow | null | undefined): string | undefined {\n return item && workItemPhaseSemantics(boards, item) ? item.stages[0] : undefined;\n}\n\nfunction itemInRuleStage(boards: BoardRegistry, item: WorkItemRow | null | undefined): item is WorkItemRow {\n return ruleStage(boards, item) !== undefined;\n}\n\ntype PersistedMessageReader = {\n listMessages(input: {\n threadId: string;\n resourceId?: string;\n page: number;\n perPage: number;\n filter?: { dateRange?: { start?: Date } };\n orderBy: { field: 'createdAt'; direction: 'ASC' };\n }): Promise<{ messages: MastraDBMessage[]; hasMore: boolean }>;\n};\n\ntype CompletedToolResult = {\n assistantMessageId: string;\n messageCreatedAt: Date;\n toolCallId: string;\n toolName: string;\n input: FactoryRuleJsonValue;\n status: 'success' | 'error';\n value: FactoryRuleJsonValue;\n};\n\ntype RuntimeSnapshot = {\n modelId: string;\n thinkingLevel: ThinkingLevel;\n};\n\ntype ActivePhaseSnapshotBase = {\n bindingId: string;\n itemId: string;\n revision: number;\n stage: FactoryRuleStage;\n role: string;\n configVersion: string;\n status: 'active';\n};\n\ntype ActivePhaseSnapshotValue = ActivePhaseSnapshotBase & {\n board: string;\n modelId?: string;\n thinkingLevel?: ThinkingLevel;\n};\n\ntype PhaseSnapshotValue = ActivePhaseSnapshotValue | { bindingId?: string; status: 'none' };\n\nfunction reviewRuntimeFromRequestContext(requestContext: ComputeStateSignalArgs['requestContext']): RuntimeSnapshot {\n if (!requestContext || typeof requestContext.get !== 'function') {\n throw new Error('Factory review phase requires a controller request context.');\n }\n const context = requestContext.get<'controller', AgentControllerRequestContext<MastraCodeState>>('controller');\n const modelId = context?.session?.modelId.trim();\n if (!modelId) {\n throw new Error('Factory review phase requires a selected session model.');\n }\n return { modelId, thinkingLevel: resolveRequestThinkingLevel(context) };\n}\n\nfunction workItemSourceKey(item: WorkItemRow): string | null {\n const source = item.externalSource;\n return source ? `${source.integrationId}:${source.type}:${source.externalId}` : null;\n}\n\nfunction boundedError(value: unknown): FactoryRuleJsonValue {\n const message = value instanceof Error ? value.message : typeof value === 'string' ? value : 'Tool execution failed.';\n return { message: message.slice(0, 2_000) };\n}\n\nfunction boundedResult(value: unknown): FactoryRuleJsonValue {\n try {\n return normalizeFactoryRuleJsonValue(value);\n } catch {\n return { message: 'Tool result was not serializable.' };\n }\n}\n\nfunction messageParts(message: MastraDBMessage): unknown[] {\n const content = message.content as { parts?: unknown[]; toolInvocations?: unknown[] } | unknown[] | undefined;\n if (Array.isArray(content)) return content;\n if (Array.isArray(content?.parts)) return content.parts;\n return Array.isArray(content?.toolInvocations) ? content.toolInvocations : [];\n}\n\nfunction completedStepToolCallIds(steps: unknown[]): Set<string> {\n const ids = new Set<string>();\n for (const rawStep of steps) {\n if (!rawStep || typeof rawStep !== 'object') continue;\n const toolResults = (rawStep as { toolResults?: unknown[] }).toolResults;\n if (!Array.isArray(toolResults)) continue;\n for (const rawResult of toolResults) {\n if (!rawResult || typeof rawResult !== 'object') continue;\n const toolCallId = (rawResult as { toolCallId?: unknown }).toolCallId;\n if (typeof toolCallId === 'string') ids.add(toolCallId);\n }\n }\n return ids;\n}\n\nfunction completedToolResults(message: MastraDBMessage): CompletedToolResult[] {\n if (message.role !== 'assistant') return [];\n const createdAt = message.createdAt instanceof Date ? message.createdAt : new Date(message.createdAt);\n const completed: CompletedToolResult[] = [];\n for (const rawPart of messageParts(message)) {\n if (!rawPart || typeof rawPart !== 'object') continue;\n const part = rawPart as Record<string, unknown>;\n const invocation =\n part.type === 'tool-invocation' && part.toolInvocation && typeof part.toolInvocation === 'object'\n ? (part.toolInvocation as Record<string, unknown>)\n : part;\n const state = invocation.state;\n if (state !== 'result' && state !== 'error') continue;\n const toolCallId = invocation.toolCallId;\n const toolName = invocation.toolName ?? invocation.name;\n if (typeof toolCallId !== 'string' || typeof toolName !== 'string') continue;\n completed.push({\n assistantMessageId: message.id,\n messageCreatedAt: createdAt,\n toolCallId,\n toolName: toolName.slice(0, 256),\n input: boundedResult(invocation.args ?? {}),\n status: state === 'error' ? 'error' : 'success',\n value: state === 'error' ? boundedError(invocation.result ?? invocation.error) : boundedResult(invocation.result),\n });\n }\n return completed;\n}\n\nfunction currentCompletedToolMessage(\n messages: MastraDBMessage[],\n toolCallIds: ReadonlySet<string>,\n): MastraDBMessage | undefined {\n for (let index = messages.length - 1; index >= 0; index -= 1) {\n const message = messages[index]!;\n if (completedToolResults(message).some(result => toolCallIds.has(result.toolCallId))) return message;\n }\n return undefined;\n}\n\nfunction phaseCacheKey(value: ActivePhaseSnapshotValue, linked: WorkItemRow[]): string {\n return createHash('sha256')\n .update(\n JSON.stringify({\n ...value,\n linked: linked.map(item => [item.id, item.revision, item.stages[0]]),\n }),\n )\n .digest('hex');\n}\n\nfunction escapeText(value: string): string {\n return value.replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>');\n}\n\nfunction phaseFromSignal(signal: { metadata?: Record<string, unknown> } | undefined): PhaseSnapshotValue | undefined {\n return (signal?.metadata?.value as { phase?: PhaseSnapshotValue } | undefined)?.phase;\n}\n\nfunction latestPhase(args: ComputeStateSignalArgs): PhaseSnapshotValue | undefined {\n for (const signal of [...args.activeStateSignals].reverse()) {\n const phase = phaseFromSignal(signal);\n if (phase) return phase;\n }\n return phaseFromSignal(args.lastSnapshot);\n}\n\nasync function withRuleTimeout<T>(operation: Promise<T>): Promise<T> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n operation,\n new Promise<never>((_, reject) => {\n timer = setTimeout(() => reject(new Error('FACTORY_RULE_TIMEOUT')), RULE_TIMEOUT_MS);\n }),\n ]);\n } finally {\n if (timer) clearTimeout(timer);\n }\n}\n\nexport class FactoryPhaseStateProcessor implements Processor<'factory-phase'> {\n readonly id = STATE_ID;\n readonly stateId = STATE_ID;\n\n constructor(\n private readonly options: {\n configVersion: string;\n storage: WorkItemsStorage;\n boards: BoardRegistry;\n transitionService?: Pick<FactoryTransitionService, 'transition'>;\n messageReader?: PersistedMessageReader;\n recordPullRequestProvenance?: (input: {\n binding: FactoryRunBindingRecord;\n item: WorkItemRow;\n assistantMessageId: string;\n toolCallId: string;\n toolName: string;\n toolInput: FactoryRuleJsonValue;\n toolResult: FactoryRuleJsonValue;\n status: 'success' | 'error';\n }) => Promise<void>;\n },\n ) {}\n\n async processInputStep(args: ProcessInputStepArgs): Promise<MessageList | undefined> {\n const address = getFactorySessionCoordinates(args.requestContext);\n if (!address) return;\n const binding = await this.options.storage.findRunBindingBySession(address);\n if (!binding || binding.status !== 'active') return;\n const completedToolCallIds = completedStepToolCallIds(args.steps);\n const completedMessage = currentCompletedToolMessage(args.messages, completedToolCallIds);\n if (completedMessage) {\n await this.ingestMessages(binding, [completedMessage], completedToolCallIds);\n }\n }\n\n async computeStateSignal(args: ComputeStateSignalArgs): Promise<ComputeStateSignalResult> {\n const address = getFactorySessionCoordinates(args.requestContext);\n if (!address) return;\n const binding = await this.options.storage.findRunBindingBySession(address);\n const prior = latestPhase(args);\n const hasBase = Boolean(args.lastSnapshot) && args.contextWindow.hasSnapshot;\n\n if (!binding) return;\n if (binding.status !== 'active') {\n if (!hasBase || prior?.status !== 'active') return;\n return {\n id: STATE_ID,\n cacheKey: `factory:none:${prior.bindingId ?? 'revoked'}`,\n mode: 'snapshot',\n tagName: 'factory-phase',\n contents: '\\n',\n value: { phase: { status: 'none' } },\n attributes: { status: 'none' },\n metadata: { value: { phase: { status: 'none' } } },\n };\n }\n\n const item = await this.options.storage.get({ orgId: binding.orgId, id: binding.workItemId });\n const stage = ruleStage(this.options.boards, item);\n if (!item || !stage) return;\n const allItems = await this.options.storage.list({\n orgId: binding.orgId,\n factoryProjectId: binding.factoryProjectId,\n });\n const linked = allItems\n .filter(candidate => candidate.parentWorkItemId === item.id || item.parentWorkItemId === candidate.id)\n .slice(0, MAX_LINKED_ITEMS);\n const board = boardForWorkItem(item);\n const baseValue: ActivePhaseSnapshotBase = {\n status: 'active',\n bindingId: binding.id,\n itemId: item.id,\n revision: item.revision,\n stage,\n role: binding.role,\n configVersion: this.options.configVersion,\n };\n const value: ActivePhaseSnapshotValue =\n board === 'review'\n ? { ...baseValue, board, ...reviewRuntimeFromRequestContext(args.requestContext) }\n : { ...baseValue, board };\n const cacheKey = phaseCacheKey(value, linked);\n if (hasBase && (args.tracking?.currentCacheKey ?? args.lastSnapshot?.metadata?.state?.cacheKey) === cacheKey)\n return;\n\n const linkedText = linked.length\n ? `\\nLinked items: ${linked.map(candidate => `${workItemSource(candidate.externalSource)} ${candidate.title}`).join('; ')}`\n : '';\n const phaseLabel = this.options.boards.get(board)?.phases[stage]?.title ?? stage;\n const runtime =\n value.modelId && value.thinkingLevel ? { modelId: value.modelId, thinkingLevel: value.thinkingLevel } : null;\n const snapshotContents =\n `Factory ${board} phase: ${escapeText(phaseLabel)} (${escapeText(stage)})\\n` +\n `Work item: ${escapeText(item.title)} (${item.id})\\n` +\n `Role: ${escapeText(binding.role)}\\nRevision: ${item.revision}\\nConfig: ${escapeText(this.options.configVersion)}\\n` +\n (runtime\n ? `Runtime: model=${escapeText(runtime.modelId)}, reasoning-setting=${escapeText(runtime.thinkingLevel)}\\n`\n : '') +\n ((board === 'work' || board === 'review') && stage === 'intake'\n ? 'This card rests in Intake: its work is paused. Answer questions without moving it; when the user asks to resume, request the transition into the working stage first, then continue the work in this session.\\n'\n : '') +\n `Use factory_transition_work_item with expectedRevision ${item.revision} to request a phase change.${escapeText(linkedText)}`;\n const isDelta = hasBase && prior?.status === 'active';\n return {\n id: STATE_ID,\n cacheKey,\n mode: isDelta ? 'delta' : 'snapshot',\n tagName: 'factory-phase',\n contents: isDelta ? `Factory phase update:\\n${snapshotContents}` : snapshotContents,\n value: { phase: value },\n ...(isDelta ? { delta: { phase: value } } : {}),\n attributes: {\n status: 'active',\n board,\n stage,\n role: binding.role,\n revision: item.revision,\n ...(runtime ?? {}),\n },\n metadata: { value: { phase: value } },\n };\n }\n\n async reconcileAllBoundThreads(): Promise<void> {\n if (!this.options.messageReader) return;\n const bindings = await this.options.storage.listActiveRunBindings();\n for (const binding of bindings) await this.reconcileBinding(binding);\n }\n\n async reconcileBinding(binding: FactoryRunBindingRecord): Promise<void> {\n const reader = this.options.messageReader;\n if (!reader || binding.status !== 'active') return;\n // One keyed read up front: a binding whose item is gone or no longer in a\n // single rule stage would ingest nothing, so skip the cursor and message\n // reads entirely instead of paying them on every walk.\n const item = await this.options.storage.get({ orgId: binding.orgId, id: binding.workItemId });\n if (!itemInRuleStage(this.options.boards, item)) return;\n const cursor = await this.options.storage.getToolResultCursor(binding.orgId, binding.factoryProjectId, binding.id);\n let page = 0;\n while (true) {\n const result = await reader.listMessages({\n threadId: binding.threadId,\n resourceId: binding.resourceId,\n page,\n perPage: TRANSCRIPT_PAGE_SIZE,\n ...(cursor ? { filter: { dateRange: { start: cursor.lastMessageCreatedAt } } } : {}),\n orderBy: { field: 'createdAt', direction: 'ASC' },\n });\n await this.ingestMessages(binding, result.messages, undefined, item);\n const last = result.messages.at(-1);\n if (last) {\n await this.options.storage.advanceToolResultCursor({\n bindingId: binding.id,\n orgId: binding.orgId,\n factoryProjectId: binding.factoryProjectId,\n lastMessageId: last.id,\n lastMessageCreatedAt: last.createdAt instanceof Date ? last.createdAt : new Date(last.createdAt),\n updatedAt: new Date(),\n });\n }\n if (!result.hasMore) break;\n page += 1;\n }\n }\n\n private async ingestMessages(\n binding: FactoryRunBindingRecord,\n messages: MastraDBMessage[],\n toolCallIds?: ReadonlySet<string>,\n preloadedItem?: WorkItemRow,\n ): Promise<void> {\n const item = preloadedItem ?? (await this.options.storage.get({ orgId: binding.orgId, id: binding.workItemId }));\n if (!itemInRuleStage(this.options.boards, item)) return;\n for (const message of messages) {\n for (const toolResult of completedToolResults(message)) {\n if (toolCallIds && !toolCallIds.has(toolResult.toolCallId)) continue;\n try {\n await this.options.recordPullRequestProvenance?.({\n binding,\n item,\n assistantMessageId: toolResult.assistantMessageId,\n toolCallId: toolResult.toolCallId,\n toolName: toolResult.toolName,\n toolInput: toolResult.input,\n toolResult: toolResult.value,\n status: toolResult.status,\n });\n } catch {\n // Provenance is supporting evidence and must not block authoritative rule ingress.\n }\n await this.ingestToolResult(binding, item, toolResult);\n }\n }\n }\n\n private async ingestToolResult(\n binding: FactoryRunBindingRecord,\n item: WorkItemRow,\n toolResult: CompletedToolResult,\n ): Promise<void> {\n const board = boardForWorkItem(item);\n const rule = resolveBoardToolRule(this.options.boards, board, toolResult.toolName);\n if (!rule) return;\n const ingressId = JSON.stringify([\n binding.id,\n binding.threadId,\n toolResult.assistantMessageId,\n toolResult.toolCallId,\n ]);\n const prior = await this.options.storage.getTransitionResultByIngress(\n binding.orgId,\n binding.factoryProjectId,\n ingressId,\n );\n if (prior) return;\n const context: FactoryToolResultRuleContext = {\n tenant: { orgId: binding.orgId, projectId: binding.factoryProjectId },\n actor: { type: 'agent', bindingId: binding.id, role: binding.role },\n ingress: { type: 'toolResult', id: ingressId },\n cause: `Completed ${toolResult.toolName}`,\n causalChain: [],\n configVersion: this.options.configVersion,\n item: {\n id: item.id,\n source: workItemSource(item.externalSource),\n sourceKey: workItemSourceKey(item),\n parentWorkItemId: item.parentWorkItemId,\n title: item.title,\n url: item.externalSource?.url ?? null,\n stages: item.stages,\n acceptedAt: item.acceptedAt,\n metadata: item.metadata,\n },\n board,\n itemRevision: item.revision,\n toolName: toolResult.toolName,\n threadId: binding.threadId,\n assistantMessageId: toolResult.assistantMessageId,\n toolCallId: toolResult.toolCallId,\n result: { status: toolResult.status, value: toolResult.value },\n };\n\n let decision: FactoryRuleDecision | void = undefined;\n let decisions: FactoryCommitDecision[] = [];\n let outcome: { status: 'accepted' | 'rejected'; code?: string; reason?: string } = { status: 'accepted' };\n try {\n decision = await withRuleTimeout(Promise.resolve(rule(Object.freeze(context))));\n if (decision?.type === 'reject') {\n outcome = { status: 'rejected', code: decision.code, reason: decision.reason };\n } else if (decision) {\n const validated = validateFactoryRuleDecisions([decision]);\n for (const entry of validated) assertFactoryDecisionTarget(entry, this.options.boards, board);\n decisions = validated;\n }\n } catch (error) {\n const timedOut = error instanceof Error && error.message === 'FACTORY_RULE_TIMEOUT';\n outcome = {\n status: 'rejected',\n code: timedOut ? 'timeout' : 'rule_error',\n reason: timedOut\n ? 'Factory rule evaluation timed out.'\n : error instanceof Error\n ? error.message.slice(0, 2_000)\n : 'Factory tool-result rule failed.',\n };\n }\n const committed = await this.options.storage.commitRuleEvaluation({\n orgId: binding.orgId,\n factoryProjectId: binding.factoryProjectId,\n workItemId: item.id,\n ingress: { identity: ingressId, triggerType: 'tool.result' },\n configVersion: this.options.configVersion,\n expectedRevision: item.revision,\n actor: { ...context.actor },\n outcome,\n decisions: decisions.map(entry => ({ ...entry })),\n causalChain: [],\n now: new Date(),\n });\n if (committed.status !== 'committed' || !this.options.transitionService) return;\n for (const entry of decisions) {\n if (entry.type !== 'transition') continue;\n await this.options.transitionService.transition({\n orgId: binding.orgId,\n factoryProjectId: binding.factoryProjectId,\n workItemId: item.id,\n board: entry.board,\n stage: entry.stage,\n expectedRevision: item.revision,\n actor: { type: 'system', id: 'factory-tool-result-rule' },\n ingress: { type: 'rule', identity: `decision:${entry.idempotencyKey}` },\n cause: 'tool_result_rule',\n causalChain: [{ ingressId, decisionType: entry.type }],\n });\n }\n }\n}\n"],"mappings":"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"}
|
package/dist/rules/resolve.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { BoardRegistry } from '../boards/index.js';
|
|
2
|
-
import type { FactoryRuleHandler, FactoryRuleSource, FactoryRuleStage,
|
|
2
|
+
import type { FactoryRuleHandler, FactoryRuleSource, FactoryRuleStage, FactoryStageRuleContext } from './types.js';
|
|
3
3
|
export interface ResolvedFactoryStageRule {
|
|
4
4
|
phase: 'exit' | 'enter';
|
|
5
5
|
handler: FactoryRuleHandler<FactoryStageRuleContext>;
|
|
@@ -12,6 +12,4 @@ export declare function resolveFactoryStageRules(boardRegistry: BoardRegistry, i
|
|
|
12
12
|
initialEntry?: boolean;
|
|
13
13
|
reenter?: boolean;
|
|
14
14
|
}): ResolvedFactoryStageRule[];
|
|
15
|
-
export declare function resolveFactoryToolRule(rules: FactoryRules, toolName: string): FactoryToolRuleLeaf['onResult'];
|
|
16
|
-
export type ResolvedFactoryToolRule = FactoryRuleHandler<FactoryToolResultRuleContext>;
|
|
17
15
|
//# sourceMappingURL=resolve.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../src/rules/resolve.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../src/rules/resolve.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAEnH,MAAM,WAAW,wBAAwB;IACvC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;IACxB,OAAO,EAAE,kBAAkB,CAAC,uBAAuB,CAAC,CAAC;CACtD;AAED,wBAAgB,wBAAwB,CACtC,aAAa,EAAE,aAAa,EAC5B,KAAK,EAAE;IACL,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,iBAAiB,CAAC;IAC1B,SAAS,EAAE,gBAAgB,CAAC;IAC5B,OAAO,EAAE,gBAAgB,CAAC;IAC1B,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,GACA,wBAAwB,EAAE,CAc5B"}
|
package/dist/rules/resolve.js
CHANGED
|
@@ -17,10 +17,7 @@ function resolveFactoryStageRules(boardRegistry, input) {
|
|
|
17
17
|
});
|
|
18
18
|
return resolved;
|
|
19
19
|
}
|
|
20
|
-
function resolveFactoryToolRule(rules, toolName) {
|
|
21
|
-
return rules.tools[toolName]?.onResult;
|
|
22
|
-
}
|
|
23
20
|
//#endregion
|
|
24
|
-
export { resolveFactoryStageRules
|
|
21
|
+
export { resolveFactoryStageRules };
|
|
25
22
|
|
|
26
23
|
//# sourceMappingURL=resolve.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolve.js","names":[],"sources":["../../src/rules/resolve.ts"],"sourcesContent":["import type { BoardRegistry } from '../boards/index.js';\nimport type {
|
|
1
|
+
{"version":3,"file":"resolve.js","names":[],"sources":["../../src/rules/resolve.ts"],"sourcesContent":["import type { BoardRegistry } from '../boards/index.js';\nimport type { FactoryRuleHandler, FactoryRuleSource, FactoryRuleStage, FactoryStageRuleContext } from './types.js';\n\nexport interface ResolvedFactoryStageRule {\n phase: 'exit' | 'enter';\n handler: FactoryRuleHandler<FactoryStageRuleContext>;\n}\n\nexport function resolveFactoryStageRules(\n boardRegistry: BoardRegistry,\n input: {\n board: string;\n source: FactoryRuleSource;\n fromStage: FactoryRuleStage;\n toStage: FactoryRuleStage;\n initialEntry?: boolean;\n reenter?: boolean;\n },\n): ResolvedFactoryStageRule[] {\n if (input.fromStage === input.toStage && !input.initialEntry && !input.reenter) return [];\n const boardRules = boardRegistry.get(input.board)?.rules;\n if (!boardRules) return [];\n const resolved: ResolvedFactoryStageRule[] = [];\n // Same-stage reentry re-runs the stage's entry work; the item never left the\n // stage, so its exit rules must not fire.\n const sameStageReentry = input.fromStage === input.toStage && input.reenter === true;\n const onExit =\n input.initialEntry || sameStageReentry ? undefined : boardRules[input.fromStage]?.[input.source]?.onExit;\n if (onExit) resolved.push({ phase: 'exit', handler: onExit });\n const onEnter = boardRules[input.toStage]?.[input.source]?.onEnter;\n if (onEnter) resolved.push({ phase: 'enter', handler: onEnter });\n return resolved;\n}\n"],"mappings":";AAQA,SAAgB,yBACd,eACA,OAQ4B;CAC5B,IAAI,MAAM,cAAc,MAAM,WAAW,CAAC,MAAM,gBAAgB,CAAC,MAAM,SAAS,OAAO,CAAC;CACxF,MAAM,aAAa,cAAc,IAAI,MAAM,KAAK,CAAC,EAAE;CACnD,IAAI,CAAC,YAAY,OAAO,CAAC;CACzB,MAAM,WAAuC,CAAC;CAG9C,MAAM,mBAAmB,MAAM,cAAc,MAAM,WAAW,MAAM,YAAY;CAChF,MAAM,SACJ,MAAM,gBAAgB,mBAAmB,KAAA,IAAY,WAAW,MAAM,UAAU,GAAG,MAAM,OAAO,EAAE;CACpG,IAAI,QAAQ,SAAS,KAAK;EAAE,OAAO;EAAQ,SAAS;CAAO,CAAC;CAC5D,MAAM,UAAU,WAAW,MAAM,QAAQ,GAAG,MAAM,OAAO,EAAE;CAC3D,IAAI,SAAS,SAAS,KAAK;EAAE,OAAO;EAAS,SAAS;CAAQ,CAAC;CAC/D,OAAO;AACT"}
|
|
@@ -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;
|
|
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,EAAE,MAAM,CAAC;IACnB,8FAA8F;IAC9F,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,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;AA8B1D,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;CA0HjF"}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { boardForWorkItem } from "../boards/semantics.js";
|
|
2
|
+
import "../boards/index.js";
|
|
1
3
|
import { hydrateFactorySession } from "../session/factory-session.js";
|
|
2
4
|
import { RequestContext } from "@mastra/core/request-context";
|
|
3
5
|
//#region src/rules/start-coordinator.ts
|
|
@@ -125,7 +127,7 @@ var FactoryStartCoordinator = class {
|
|
|
125
127
|
orgId: request.orgId,
|
|
126
128
|
factoryProjectId: request.factoryProjectId,
|
|
127
129
|
workItemId: prepared.item.id,
|
|
128
|
-
board: prepared.item
|
|
130
|
+
board: boardForWorkItem(prepared.item),
|
|
129
131
|
stage: destinationStage,
|
|
130
132
|
expectedRevision: prepared.item.revision,
|
|
131
133
|
actor: {
|
|
@@ -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';\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 kickoffKey: string;\n /** Where the arrival path lands the card; a session opened on an existing card names none. */\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\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 await session.thread.setSetting({ key: 'factorySessionId', value: request.sessionId });\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 // Factory runs resolve model credentials org > user: the org's shared keys\n // win, with the acting user's personal credentials as a fallback — a board\n // run should never silently prefer whoever kicked it off. The flag rides\n // the stashed user even when a caller-provided context already has one.\n const existingUser = requestContext.get('user');\n if (existingUser && typeof existingUser === 'object') {\n requestContext.set('user', { ...existingUser, orgFirstCredentials: true });\n } else {\n requestContext.set('user', {\n workosId: request.userId,\n organizationId: request.orgId,\n orgFirstCredentials: true,\n });\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 = request.workItem.input.externalSource?.type === 'pull-request';\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 // The authoritative org id for every downstream identity read (the\n // memory seam's organizationId): the session owner is a USER id, not an\n // org, so it must never be improvised from ownerId.\n factoryOrgId: request.orgId,\n ...(untrustedCheckout ? { untrustedCheckout: true, ...(baseRef ? { baseRef } : {}) } : {}),\n });\n // Board runs are org-shared: hydrate with the factory's default model and\n // the project's shared memory settings (falling back to the built-in\n // defaults), never any individual user's stored settings.\n await hydrateFactorySession(session, {\n orgId: request.orgId,\n factoryProjectId: request.factoryProjectId,\n defaultModelId: request.defaultModelId,\n memorySettings: this.#memorySettings,\n });\n // The tool is `requireApproval`, and that prompt parks the run whether or not\n // someone pressed Start — a person is reading the plan, not an approval queue.\n // Starting the run was already the say-so; the rules engine still governs.\n await session.permissions.setForTool({ toolName: 'factory_transition_work_item', policy: 'allow' });\n const threadId = await configureThread(session, request);\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: null,\n });\n await session.thread.setSetting({ key: 'factoryWorkItemId', value: prepared.item.id });\n\n let revision = prepared.item.revision;\n const destinationStage = request.destinationStage;\n if (destinationStage && (prepared.item.stages.length !== 1 || prepared.item.stages[0] !== 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: 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 await storage.markPendingStart(prepared.binding.id, 'sent');\n prepared.pendingStart.status = 'sent';\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":";;;AA6BA,IAAa,8BAAb,cAAiD,MAAM;CACrD;CAEA,YAAY,QAAkE;EAC5E,MAAM,OAAO,MAAM;EACnB,KAAK,OAAO;EACZ,KAAK,SAAS;CAChB;AACF;AAiBA,eAAe,qBACb,SACA,SAC+B;CAC/B,MAAM,UAAU,MAAM,QAAQ,SAAS,eAAe,QAAQ,SAAS;CACvE,IAAI,CAAC,WAAW,QAAQ,UAAU,QAAQ,SAAS,QAAQ,WAAW,QAAQ,QAC5E,MAAM,IAAI,MAAM,2BAA2B;CAE7C,MAAM,oBAAoB,MAAM,QAAQ,oBAAoB,IAAI;EAC9D,OAAO,QAAQ;EACf,IAAI,QAAQ;CACd,CAAC;CACD,IAAI,CAAC,mBAAmB,MAAM,IAAI,MAAM,sCAAsC;CAC9E,MAAM,aAAa,MAAM,QAAQ,YAAY,IAAI;EAAE,OAAO,QAAQ;EAAO,IAAI,kBAAkB;CAAa,CAAC;CAC7G,IAAI,CAAC,cAAc,WAAW,qBAAqB,QAAQ,kBACzD,MAAM,IAAI,MAAM,iDAAiD;CAEnE,OAAO;AACT;AAEA,eAAe,gBAAgB,SAAyB,SAA+C;CACrG,MAAM,WAAW,QAAQ,OAAO,UAAU;CAC1C,MAAM,QAAQ,OAAO,OAAO,EAAE,OAAO,QAAQ,YAAY,CAAC;CAC1D,MAAM,QAAQ,OAAO,WAAW;EAAE,KAAK;EAAoB,OAAO,QAAQ;CAAU,CAAC;CACrF,OAAO;AACT;AAEA,IAAa,0BAAb,MAAqC;CACnC;CACA;CACA;CACA;CACA;CAEA,YACE,YACA,SACA,mBACA,eACA,gBACA;EACA,KAAKA,cAAc;EACnB,KAAKC,WAAW;EAChB,KAAKC,qBAAqB;EAC1B,KAAKC,iBAAiB;EACtB,KAAKC,kBAAkB;CACzB;CAEA,MAAM,QAAQ,SAAmE;EAC/E,MAAM,UAAU,KAAKH;EACrB,IAAI,CAAC,KAAKE,gBAAgB,MAAM,IAAI,MAAM,+CAA+C;EACzF,MAAM,gBAAgB,MAAM,qBAAqB,KAAKA,gBAAgB,OAAO;EAC7E,MAAM,iBAAiB,QAAQ,kBAAkB,IAAI,eAAe;EAKpE,MAAM,eAAe,eAAe,IAAI,MAAM;EAC9C,IAAI,gBAAgB,OAAO,iBAAiB,UAC1C,eAAe,IAAI,QAAQ;GAAE,GAAG;GAAc,qBAAqB;EAAK,CAAC;OAEzE,eAAe,IAAI,QAAQ;GACzB,UAAU,QAAQ;GAClB,gBAAgB,QAAQ;GACxB,qBAAqB;EACvB,CAAC;EAMH,MAAM,oBAAoB,QAAQ,SAAS,MAAM,gBAAgB,SAAS;EAI1E,MAAM,qBAAqB,QAAQ,SAAS,MAAM,UAAU;EAC5D,MAAM,WACH,cAAc,cAAc,KAAA,OAC5B,OAAO,uBAAuB,YAAY,qBAAqB,qBAAqB,KAAA;EACvF,MAAM,cAAc;GAClB,kBAAkB,QAAQ;GAC1B,qBAAqB,cAAc;EACrC;EACA,MAAM,UAAU,MAAM,KAAKH,YAAY,cAAc;GACnD,IAAI,cAAc;GAClB,SAAS,QAAQ;GACjB,YAAY,cAAc;GAC1B,UAAU,cAAc;GACxB;GACA,MAAM;EACR,CAAC;EAQD,MAAM,QAAQ,MAAM,IAAI;GACtB,GAAG;GAIH,cAAc,QAAQ;GACtB,GAAI,oBAAoB;IAAE,mBAAmB;IAAM,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC;GAAG,IAAI,CAAC;EAC1F,CAAC;EAID,MAAM,sBAAsB,SAAS;GACnC,OAAO,QAAQ;GACf,kBAAkB,QAAQ;GAC1B,gBAAgB,QAAQ;GACxB,gBAAgB,KAAKI;EACvB,CAAC;EAID,MAAM,QAAQ,YAAY,WAAW;GAAE,UAAU;GAAgC,QAAQ;EAAQ,CAAC;EAClG,MAAM,WAAW,MAAM,gBAAgB,SAAS,OAAO;EACvD,MAAM,WAAW,MAAM,QAAQ,gBAAgB;GAC7C,OAAO,QAAQ;GACf,QAAQ,QAAQ;GAChB,kBAAkB,QAAQ;GAC1B,UAAU;IAAE,IAAI,QAAQ,SAAS;IAAI,OAAO,QAAQ,SAAS;GAAM;GACnE,MAAM,QAAQ,SAAS;GACvB,SAAS;IAAE,WAAW,cAAc;IAAW,QAAQ,cAAc;IAAQ;GAAS;GACtF,YAAY,cAAc;GAC1B,YAAY,QAAQ;GACpB,gBAAgB;EAClB,CAAC;EACD,MAAM,QAAQ,OAAO,WAAW;GAAE,KAAK;GAAqB,OAAO,SAAS,KAAK;EAAG,CAAC;EAErF,IAAI,WAAW,SAAS,KAAK;EAC7B,MAAM,mBAAmB,QAAQ;EACjC,IAAI,qBAAqB,SAAS,KAAK,OAAO,WAAW,KAAK,SAAS,KAAK,OAAO,OAAO,mBAAmB;GAC3G,IAAI,CAAC,KAAKF,oBAAoB,MAAM,IAAI,MAAM,4CAA4C;GAC1F,MAAM,aAAa,MAAM,KAAKA,mBAAmB,WAAW;IAC1D,OAAO,QAAQ;IACf,kBAAkB,QAAQ;IAC1B,YAAY,SAAS,KAAK;IAC1B,OAAO,SAAS,KAAK,gBAAgB,SAAS,iBAAiB,WAAW;IAC1E,OAAO;IACP,kBAAkB,SAAS,KAAK;IAChC,OAAO;KAAE,MAAM;KAAS,IAAI,QAAQ;IAAO;IAC3C,SAAS;KAAE,MAAM;KAAS,UAAU,SAAS,QAAQ,WAAW;IAAa;IAC7E,OAAO;GACT,CAAC;GACD,IAAI,WAAW,WAAW,YAAY;IACpC,MAAM,QAAQ,iBAAiB,SAAS,QAAQ,IAAI,UAAU,WAAW,MAAM;IAC/E,MAAM,IAAI,4BAA4B,UAAU;GAClD;GACA,WAAW,WAAW;EACxB;EAEA,MAAM,QAAQ,iBAAiB,SAAS,QAAQ,IAAI,MAAM;EAC1D,SAAS,aAAa,SAAS;EAE/B,OAAO;GACL,YAAY,SAAS,KAAK;GAC1B,WAAW,SAAS,QAAQ;GAC5B;GACA,YAAY,cAAc;GAC1B,WAAW,cAAc;GACzB,QAAQ,cAAc;GACtB;GACA,eAAe,SAAS,aAAa;GACrC,UAAU,SAAS;EACrB;CACF;AACF"}
|
|
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';\n\nimport { boardForWorkItem } from '../boards/index.js';\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 kickoffKey: string;\n /** Where the arrival path lands the card; a session opened on an existing card names none. */\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\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 await session.thread.setSetting({ key: 'factorySessionId', value: request.sessionId });\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 // Factory runs resolve model credentials org > user: the org's shared keys\n // win, with the acting user's personal credentials as a fallback — a board\n // run should never silently prefer whoever kicked it off. The flag rides\n // the stashed user even when a caller-provided context already has one.\n const existingUser = requestContext.get('user');\n if (existingUser && typeof existingUser === 'object') {\n requestContext.set('user', { ...existingUser, orgFirstCredentials: true });\n } else {\n requestContext.set('user', {\n workosId: request.userId,\n organizationId: request.orgId,\n orgFirstCredentials: true,\n });\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 = request.workItem.input.externalSource?.type === 'pull-request';\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 // The authoritative org id for every downstream identity read (the\n // memory seam's organizationId): the session owner is a USER id, not an\n // org, so it must never be improvised from ownerId.\n factoryOrgId: request.orgId,\n ...(untrustedCheckout ? { untrustedCheckout: true, ...(baseRef ? { baseRef } : {}) } : {}),\n });\n // Board runs are org-shared: hydrate with the factory's default model and\n // the project's shared memory settings (falling back to the built-in\n // defaults), never any individual user's stored settings.\n await hydrateFactorySession(session, {\n orgId: request.orgId,\n factoryProjectId: request.factoryProjectId,\n defaultModelId: request.defaultModelId,\n memorySettings: this.#memorySettings,\n });\n // The tool is `requireApproval`, and that prompt parks the run whether or not\n // someone pressed Start — a person is reading the plan, not an approval queue.\n // Starting the run was already the say-so; the rules engine still governs.\n await session.permissions.setForTool({ toolName: 'factory_transition_work_item', policy: 'allow' });\n const threadId = await configureThread(session, request);\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: null,\n });\n await session.thread.setSetting({ key: 'factoryWorkItemId', value: prepared.item.id });\n\n let revision = prepared.item.revision;\n const destinationStage = request.destinationStage;\n if (destinationStage && (prepared.item.stages.length !== 1 || prepared.item.stages[0] !== 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: boardForWorkItem(prepared.item),\n stage: 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 await storage.markPendingStart(prepared.binding.id, 'sent');\n prepared.pendingStart.status = 'sent';\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 +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;
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/rules/tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAKnE,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;AA8BxE,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,CAoE5B"}
|
package/dist/rules/tools.js
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BOARD_IDENTIFIER_RE } from "./validation.js";
|
|
2
|
+
import { boardForWorkItem } from "../boards/semantics.js";
|
|
3
|
+
import { FACTORY_TRIAGE_TYPES } from "./types.js";
|
|
4
|
+
import "../boards/index.js";
|
|
2
5
|
import { resolveFactorySessionAddress } from "./binding-context.js";
|
|
3
6
|
import { createTool } from "@mastra/core/tools";
|
|
4
7
|
import { z } from "zod";
|
|
5
8
|
//#region src/rules/tools.ts
|
|
6
9
|
const MAX_RATIONALE_LENGTH = 1e3;
|
|
7
10
|
const transitionInputSchema = z.object({
|
|
8
|
-
stage: z.
|
|
11
|
+
stage: z.string().max(128).regex(BOARD_IDENTIFIER_RE),
|
|
9
12
|
expectedRevision: z.number().int().positive(),
|
|
10
|
-
rationale: z.string().trim().min(1).transform((value) => value.length <= MAX_RATIONALE_LENGTH ? value : `${value.slice(0, MAX_RATIONALE_LENGTH - 1)}…`)
|
|
13
|
+
rationale: z.string().trim().min(1).transform((value) => value.length <= MAX_RATIONALE_LENGTH ? value : `${value.slice(0, MAX_RATIONALE_LENGTH - 1)}…`),
|
|
14
|
+
triageType: z.enum(FACTORY_TRIAGE_TYPES).optional()
|
|
11
15
|
}).strict();
|
|
12
16
|
const triageTransitionInputSchema = transitionInputSchema.extend({ triageType: z.enum(FACTORY_TRIAGE_TYPES) });
|
|
13
|
-
function boardForSource(type) {
|
|
14
|
-
return type === "pull-request" ? "review" : "work";
|
|
15
|
-
}
|
|
16
17
|
async function createFactoryTransitionTools(options) {
|
|
17
18
|
const resolution = await resolveFactorySessionAddress({
|
|
18
19
|
requestContext: options.requestContext,
|
|
@@ -22,13 +23,21 @@ async function createFactoryTransitionTools(options) {
|
|
|
22
23
|
if (!resolution) return {};
|
|
23
24
|
const availableBinding = resolution.binding ?? await options.storage.findActiveRunBinding(resolution.address);
|
|
24
25
|
if (!availableBinding) return {};
|
|
25
|
-
|
|
26
|
+
let isTriage = false;
|
|
27
|
+
if (availableBinding.role === "triage") {
|
|
28
|
+
const item = await options.storage.get({
|
|
29
|
+
orgId: availableBinding.orgId,
|
|
30
|
+
id: availableBinding.workItemId
|
|
31
|
+
});
|
|
32
|
+
if (!item) return {};
|
|
33
|
+
isTriage = boardForWorkItem(item) === "work";
|
|
34
|
+
}
|
|
26
35
|
return { factory_transition_work_item: createTool({
|
|
27
36
|
id: "factory_transition_work_item",
|
|
28
37
|
description: isTriage ? "Report the triage classification and request a governed stage transition for the Factory work item exactly bound to this thread. Only bugs may request Planning autonomously; closure outcomes may request a terminal stage. Feature requests and other non-bug classifications that remain open must stay in their current Intake or Triage stage for maintainer approval." : "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.",
|
|
29
38
|
inputSchema: isTriage ? triageTransitionInputSchema : transitionInputSchema,
|
|
30
39
|
requireApproval: true,
|
|
31
|
-
execute: async ({ stage, expectedRevision, rationale,
|
|
40
|
+
execute: async ({ stage, expectedRevision, rationale, triageType: requestedTriageType }, execution) => {
|
|
32
41
|
const currentAddress = (await resolveFactorySessionAddress({
|
|
33
42
|
requestContext: execution.requestContext,
|
|
34
43
|
storage: options.storage,
|
|
@@ -43,12 +52,13 @@ async function createFactoryTransitionTools(options) {
|
|
|
43
52
|
id: binding.workItemId
|
|
44
53
|
});
|
|
45
54
|
if (!item) throw new Error("Bound Factory work item not found.");
|
|
46
|
-
const
|
|
55
|
+
const board = boardForWorkItem(item);
|
|
56
|
+
const triageType = board === "work" && binding.role === "triage" ? requestedTriageType : void 0;
|
|
47
57
|
return await options.transitionService.transition({
|
|
48
58
|
orgId: binding.orgId,
|
|
49
59
|
factoryProjectId: binding.factoryProjectId,
|
|
50
60
|
workItemId: binding.workItemId,
|
|
51
|
-
board
|
|
61
|
+
board,
|
|
52
62
|
stage,
|
|
53
63
|
expectedRevision,
|
|
54
64
|
actor: {
|