@mastra/factory 0.12.0-alpha.0 → 0.12.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/dist/factory.d.ts.map +1 -1
  3. package/dist/factory.js +6 -1
  4. package/dist/factory.js.map +1 -1
  5. package/dist/feed-events.d.ts +13 -0
  6. package/dist/feed-events.d.ts.map +1 -0
  7. package/dist/feed-events.js +25 -0
  8. package/dist/feed-events.js.map +1 -0
  9. package/dist/integrations/github/issue-reconciler.d.ts.map +1 -1
  10. package/dist/integrations/github/issue-reconciler.js +22 -14
  11. package/dist/integrations/github/issue-reconciler.js.map +1 -1
  12. package/dist/integrations/github/reconcile-worker.js +1 -1
  13. package/dist/integrations/github/rules.d.ts +6 -0
  14. package/dist/integrations/github/rules.d.ts.map +1 -1
  15. package/dist/integrations/github/rules.js +58 -12
  16. package/dist/integrations/github/rules.js.map +1 -1
  17. package/dist/integrations/issue-reconcile-worker.js +1 -1
  18. package/dist/integrations/platform/github/event-worker.js +1 -1
  19. package/dist/integrations/platform/linear/event-worker.js +1 -1
  20. package/dist/routes/attention-activity.d.ts +24 -0
  21. package/dist/routes/attention-activity.d.ts.map +1 -0
  22. package/dist/routes/attention-activity.js +143 -0
  23. package/dist/routes/attention-activity.js.map +1 -0
  24. package/dist/routes/attention-providers.d.ts +31 -1
  25. package/dist/routes/attention-providers.d.ts.map +1 -1
  26. package/dist/routes/attention-providers.js +4 -4
  27. package/dist/routes/attention-providers.js.map +1 -1
  28. package/dist/routes/attention.d.ts.map +1 -1
  29. package/dist/routes/attention.js +39 -11
  30. package/dist/routes/attention.js.map +1 -1
  31. package/dist/routes/surface.d.ts.map +1 -1
  32. package/dist/routes/surface.js +4 -3
  33. package/dist/routes/surface.js.map +1 -1
  34. package/dist/routes/work-items.d.ts.map +1 -1
  35. package/dist/routes/work-items.js +11 -1
  36. package/dist/routes/work-items.js.map +1 -1
  37. package/dist/rules/defaults.d.ts.map +1 -1
  38. package/dist/rules/defaults.js +20 -8
  39. package/dist/rules/defaults.js.map +1 -1
  40. package/dist/rules/dispatcher.d.ts.map +1 -1
  41. package/dist/rules/dispatcher.js +24 -10
  42. package/dist/rules/dispatcher.js.map +1 -1
  43. package/dist/rules/processor.d.ts.map +1 -1
  44. package/dist/rules/processor.js +2 -3
  45. package/dist/rules/processor.js.map +1 -1
  46. package/dist/rules/transition-service.d.ts +2 -2
  47. package/dist/rules/transition-service.d.ts.map +1 -1
  48. package/dist/rules/transition-service.js +33 -12
  49. package/dist/rules/transition-service.js.map +1 -1
  50. package/dist/rules/types.d.ts +27 -1
  51. package/dist/rules/types.d.ts.map +1 -1
  52. package/dist/rules/types.js +37 -1
  53. package/dist/rules/types.js.map +1 -1
  54. package/dist/rules/validation.d.ts.map +1 -1
  55. package/dist/rules/validation.js +3 -1
  56. package/dist/rules/validation.js.map +1 -1
  57. package/dist/storage/domains/comments/activity.d.ts +40 -0
  58. package/dist/storage/domains/comments/activity.d.ts.map +1 -0
  59. package/dist/storage/domains/comments/activity.js +94 -0
  60. package/dist/storage/domains/comments/activity.js.map +1 -0
  61. package/dist/storage/domains/comments/base.d.ts +13 -1
  62. package/dist/storage/domains/comments/base.d.ts.map +1 -1
  63. package/dist/storage/domains/comments/base.js +41 -3
  64. package/dist/storage/domains/comments/base.js.map +1 -1
  65. package/dist/storage/domains/comments/domain.d.ts +4 -1
  66. package/dist/storage/domains/comments/domain.d.ts.map +1 -1
  67. package/dist/storage/domains/comments/domain.js +17 -5
  68. package/dist/storage/domains/comments/domain.js.map +1 -1
  69. package/dist/storage/domains/comments/parse.d.ts +1 -0
  70. package/dist/storage/domains/comments/parse.d.ts.map +1 -1
  71. package/dist/storage/domains/comments/parse.js +2 -2
  72. package/dist/storage/domains/comments/parse.js.map +1 -1
  73. package/dist/storage/domains/comments/routes.d.ts +2 -0
  74. package/dist/storage/domains/comments/routes.d.ts.map +1 -1
  75. package/dist/storage/domains/comments/routes.js +49 -3
  76. package/dist/storage/domains/comments/routes.js.map +1 -1
  77. package/dist/storage/domains/comments/schema.d.ts +1 -0
  78. package/dist/storage/domains/comments/schema.d.ts.map +1 -1
  79. package/dist/storage/domains/comments/schema.js +45 -1
  80. package/dist/storage/domains/comments/schema.js.map +1 -1
  81. package/dist/storage/domains/work-items/base.d.ts +18 -3
  82. package/dist/storage/domains/work-items/base.d.ts.map +1 -1
  83. package/dist/storage/domains/work-items/base.js +95 -38
  84. package/dist/storage/domains/work-items/base.js.map +1 -1
  85. package/package.json +6 -6
@@ -1 +1 @@
1
- {"version":3,"file":"defaults.js","names":[],"sources":["../../src/rules/defaults.ts"],"sourcesContent":["import type {\n FactoryBoardRuleLeaf,\n FactoryBoardRules,\n FactoryGithubRuleLeaf,\n FactoryGithubEventName,\n FactoryGithubRuleContext,\n FactoryLinearEventName,\n FactoryLinearRuleContext,\n FactoryLinearRuleLeaf,\n FactoryRules,\n FactoryRulesOverrides,\n FactoryRuleSource,\n FactoryRuleStage,\n FactoryStageRuleContext,\n FactoryToolResultRuleContext,\n FactoryToolRuleLeaf,\n} from './types.js';\nimport { assertFactoryRules, FactoryRuleValidationError } from './validation.js';\n\nexport const DEFAULT_FACTORY_RULE_VERSION = 'factory-default-v1';\n\nfunction trustedGithubActor(context: Pick<FactoryStageRuleContext, 'actor'>): boolean {\n return context.actor.type === 'github' && context.actor.trusted;\n}\n\nfunction githubActorLogin(context: Pick<FactoryStageRuleContext, 'actor'>): string | undefined {\n return context.actor.type === 'github' ? context.actor.login : undefined;\n}\n\nfunction invokeIssueInvestigation(context: FactoryStageRuleContext) {\n return {\n type: 'invokeSkill',\n idempotencyKey: `${context.ingress.id}:factory-triage`,\n role: 'triage',\n skillName: 'factory-triage',\n arguments: context.item.url ? `GitHub issue (${context.item.url})` : context.item.title,\n } as const;\n}\n\nfunction investigateTriagedIssue(context: FactoryStageRuleContext) {\n if (\n context.cause === 'run_start' ||\n (context.cause === 'linked_item_materialized' && context.fromStage === 'intake' && context.toStage === 'triage')\n ) {\n return;\n }\n return invokeIssueInvestigation(context);\n}\n\nfunction retriageGithubIssue(context: FactoryGithubRuleContext) {\n if (!context.item || context.item.source !== 'github-issue' || !context.item.url) return;\n if (context.actor.type === 'github' && context.actor.factoryAuthored) return;\n\n const reason =\n context.event === 'issueEdited'\n ? context.issueChange?.title && context.issueChange.body\n ? 'issue title and body edited'\n : context.issueChange?.title\n ? 'issue title edited'\n : 'issue body edited'\n : context.event === 'issueCommentDeleted'\n ? 'comment deleted'\n : context.event === 'issueCommentEdited'\n ? 'comment edited'\n : 'comment created';\n return {\n type: 'invokeSkill',\n idempotencyKey: `${context.ingress.id}:factory-triage`,\n role: 'triage',\n skillName: 'factory-triage',\n arguments: `Re-triage GitHub issue (${context.item.url}) after ${reason}.`,\n } as const;\n}\n\nfunction investigateTriagedLinearIssue(context: FactoryStageRuleContext) {\n const identifier = context.item.sourceKey?.startsWith('linear:')\n ? context.item.sourceKey.slice('linear:'.length)\n : context.item.title;\n return {\n type: 'invokeSkill',\n idempotencyKey: `${context.ingress.id}:factory-triage-linear`,\n role: 'triage',\n skillName: 'factory-triage',\n arguments: `Linear issue ${identifier}${context.item.url ? ` (${context.item.url})` : ''}`,\n } as const;\n}\n\nfunction planWorkItem(context: FactoryStageRuleContext) {\n return {\n type: 'invokeSkill',\n idempotencyKey: `${context.ingress.id}:factory-plan`,\n role: 'plan',\n skillName: 'factory-plan',\n arguments: context.item.url ? `Work item (${context.item.url})` : context.item.title,\n } as const;\n}\n\n// A GitHub login is alphanumeric with interior hyphens — no underscores, no\n// spaces. Checking the grammar rejects the placeholder the issue poller stamps\n// when the reporter's account is gone (`__unknown__`), which would otherwise\n// become a trailer crediting an account that does not exist.\nconst GITHUB_LOGIN = /^[a-zA-Z0-9](?:[a-zA-Z0-9]|-(?=[a-zA-Z0-9])){0,38}$/;\n\n/**\n * The reporter earns a `Co-Authored-By` trailer on the work their report caused.\n * Only a GitHub issue qualifies: Linear stamps a display name and a manual card\n * stamps nothing, and neither resolves to the GitHub identity a trailer needs.\n * Factory's own reports are skipped — crediting ourselves is noise.\n */\nfunction reporterCoAuthor(context: FactoryStageRuleContext) {\n if (context.source !== 'issue') return undefined;\n const author = context.item.metadata?.author;\n if (typeof author !== 'string' || !author) return undefined;\n if (author.endsWith('[bot]') || !GITHUB_LOGIN.test(author)) return undefined;\n return author;\n}\n\n/**\n * Building carries a prompt rather than a skill. The approved plan is already\n * the specification, so there is nothing for a skill document to add, and the\n * handoff a skill would define is unnecessary here: Building ends by opening a\n * pull request, which arrives as its own event and raises the Review card.\n */\nfunction buildWorkItem(context: FactoryStageRuleContext) {\n const subject = context.item.url ? `the approved plan for ${context.item.url}` : 'the approved plan';\n const reporter = reporterCoAuthor(context);\n // The trailer needs the reporter's numeric id, which intake does not stamp, so\n // the agent resolves it from the same issue it is already reading.\n const credit = reporter\n ? ` The work was reported by @${reporter}: credit them on every commit with a ` +\n `\\`Co-Authored-By: ${reporter} <ID+${reporter}@users.noreply.github.com>\\` trailer, ` +\n `resolving ID with \\`gh api users/${reporter} --jq .id\\`.`\n : '';\n return {\n type: 'invokeSkill',\n idempotencyKey: `${context.ingress.id}:build`,\n role: 'work',\n prompt: `Implement ${subject}. Open a pull request when the work is ready for review.${credit}`,\n } as const;\n}\n\nfunction completeIssue(context: FactoryStageRuleContext) {\n return {\n type: 'invokeSkill',\n idempotencyKey: `${context.ingress.id}:factory-complete-issue`,\n role: 'triage',\n skillName: 'factory-complete-issue',\n arguments: context.item.url ? `GitHub issue (${context.item.url})` : context.item.title,\n } as const;\n}\n\nfunction reviewPullRequest(context: FactoryStageRuleContext) {\n // A re-entry into Review (from any post-intake stage) supersedes whichever\n // review pass previously ran on this card: cancel any in-flight run before\n // dispatching a fresh one so we don't burn tokens on the stale pass and race\n // two agents on the same card. Cancellation is safe when nothing is in flight.\n const supersedes = context.fromStage !== 'intake';\n // The re-review skill only applies when a prior review pass actually completed\n // (the card is returning from `done`). A cancelled first-time review that\n // re-enters Review from `review` itself still has no prior pass to reconcile —\n // it gets the regular factory-review skill.\n const priorReviewCompleted = context.fromStage === 'done';\n const skillName = priorReviewCompleted ? 'factory-rereview' : 'factory-review';\n return {\n type: 'invokeSkill',\n idempotencyKey: `${context.ingress.id}:${skillName}`,\n role: 'review',\n skillName,\n arguments: context.item.url ? `GitHub pull request (${context.item.url})` : context.item.title,\n ...(supersedes ? { cancelInFlight: true } : {}),\n } as const;\n}\n\nfunction resultContent(value: unknown): string | undefined {\n if (typeof value === 'string') return value;\n if (!value || typeof value !== 'object' || Array.isArray(value)) return;\n const content = (value as { content?: unknown }).content;\n return typeof content === 'string' ? content : undefined;\n}\n\n// Interactive-session path only: factory-plan never calls submit_plan — it\n// advances planning → execute via factory_transition_work_item directly.\nfunction advanceApprovedPlan(context: FactoryToolResultRuleContext) {\n if (\n context.result.status !== 'success' ||\n context.board !== 'work' ||\n context.item.stages.length !== 1 ||\n context.item.stages[0] !== 'planning' ||\n context.actor.type !== 'agent' ||\n context.actor.role !== 'plan' ||\n !resultContent(context.result.value)?.startsWith('Plan approved.')\n ) {\n return;\n }\n return {\n type: 'transition',\n idempotencyKey: `${context.ingress.id}:approved-plan`,\n board: 'work',\n stage: 'execute',\n } as const;\n}\n\nfunction createdAfterFactory(createdAt: string | undefined, factoryCreatedAt: string): boolean {\n if (!createdAt) return false;\n const sourceCreatedAt = Date.parse(createdAt);\n const projectCreatedAt = Date.parse(factoryCreatedAt);\n return Number.isFinite(sourceCreatedAt) && Number.isFinite(projectCreatedAt) && sourceCreatedAt > projectCreatedAt;\n}\n\nfunction issueOpened(context: FactoryGithubRuleContext) {\n if (!context.issue) return;\n return {\n type: 'upsertLinkedWorkItem',\n idempotencyKey: `${context.ingress.id}:issue-intake`,\n board: 'work',\n source: 'github-issue',\n sourceKey: `github-issue:${context.issue.number}`,\n title: context.issue.title,\n url: context.issue.url,\n stage:\n trustedGithubActor(context) && createdAfterFactory(context.issue.createdAt, context.factory.createdAt)\n ? 'triage'\n : 'intake',\n metadata: {\n githubRepositoryId: context.repository.id,\n githubIssueNumber: context.issue.number,\n ...(githubActorLogin(context) ? { author: githubActorLogin(context) } : {}),\n assignees: context.issue.assignees ?? [],\n labels: context.issue.labels ?? [],\n },\n } as const;\n}\n\nfunction issueClosed(context: FactoryGithubRuleContext) {\n if (!context.item || context.item.source !== 'github-issue' || !context.issue) return;\n if (context.board !== 'work') return;\n // Already off the board: nothing to reconcile.\n if (context.item.stages.some(stage => stage === 'done' || stage === 'canceled')) return;\n // Issue closure is a repository fact, not third-party input — no actor trust\n // gate. `not_planned` (and `duplicate`) means abandoned, everything else is\n // completed work.\n const canceled = context.issue.stateReason === 'not_planned' || context.issue.stateReason === 'duplicate';\n return {\n type: 'transition',\n idempotencyKey: `${context.ingress.id}:issue-closed`,\n board: 'work',\n stage: canceled ? 'canceled' : 'done',\n message: {\n text:\n `GitHub issue #${context.issue.number} was closed` +\n `${context.issue.stateReason ? ` (${context.issue.stateReason})` : ''}; ` +\n `this Work card was moved to ${canceled ? 'Canceled' : 'Done'}.`,\n },\n } as const;\n}\n\nfunction pullRequestOpened(context: FactoryGithubRuleContext) {\n if (!context.pullRequest) return;\n // Trust is a repository-collaborator permission lookup, and a GitHub App bot\n // is never a collaborator — so a PR Factory opened itself scores as untrusted\n // and parks in Intake, the one class of PR whose provenance we know best.\n // Factory authorship is its own trust signal: the branch came from a Work run\n // this Factory dispatched.\n const factoryAuthored = context.actor.type === 'github' && context.actor.factoryAuthored;\n return {\n type: 'upsertLinkedWorkItem',\n idempotencyKey: `${context.ingress.id}:pull-request-intake`,\n board: 'review',\n source: 'github-pr',\n sourceKey: `github-pr:${context.pullRequest.number}`,\n title: context.pullRequest.title,\n url: context.pullRequest.url,\n stage:\n (trustedGithubActor(context) || factoryAuthored) &&\n createdAfterFactory(context.pullRequest.createdAt, context.factory.createdAt)\n ? 'review'\n : 'intake',\n metadata: {\n githubRepositoryId: context.repository.id,\n githubPullRequestNumber: context.pullRequest.number,\n factoryAuthored,\n state: context.pullRequest.state,\n draft: context.pullRequest.draft,\n merged: context.pullRequest.merged,\n assignees: context.pullRequest.assignees ?? [],\n requestedReviewers: context.pullRequest.requestedReviewers ?? [],\n labels: context.pullRequest.labels ?? [],\n headBranch: context.pullRequest.headBranch,\n baseBranch: context.pullRequest.baseBranch,\n ...(githubActorLogin(context) ? { author: githubActorLogin(context) } : {}),\n },\n } as const;\n}\n\nfunction pullRequestMerged(context: FactoryGithubRuleContext) {\n if (!context.item || !context.pullRequest?.merged) return;\n if (context.board === 'review') {\n // The event is bound to the PR's own Review card: a merged PR is finished\n // review work, so always move the card to Done. The message only reaches\n // an active session (if any) — cards without one just move, instead of\n // failing retries against a binding that never existed.\n return {\n type: 'transition',\n idempotencyKey: `${context.ingress.id}:pull-request-merged`,\n board: 'review',\n stage: 'done',\n message: {\n text:\n `Pull request #${context.pullRequest.number} merged; this Review card was moved to Done. ` +\n 'No further review is needed unless follow-up work was requested.',\n },\n } as const;\n }\n // Provenance bound the event to the originating Work item instead: remind\n // its agent to assess completion — never auto-complete the Work item.\n return {\n type: 'sendMessage',\n idempotencyKey: `${context.ingress.id}:assess-work-completion`,\n role: 'work',\n message:\n `Pull request #${context.pullRequest.number} merged. Assess whether the linked Work item is complete. ` +\n 'Do not mark it Done solely because this PR merged; use factory_transition_work_item only after verifying the work.',\n } as const;\n}\n\nfunction addressReviewFeedback(context: FactoryGithubRuleContext) {\n if (!context.item || !context.pullRequest || !context.review) return;\n // Only the Work item that authored the PR can act on the feedback. Provenance\n // binds the event there; a Review card seeing its own posted review must not\n // react to it (that would loop the reviewer against itself).\n if (context.board !== 'work') return;\n // A closed or merged pull request has no branch left to push fixes to.\n if (context.pullRequest.state !== 'open' || context.pullRequest.merged) return;\n // `approved` needs no work, and `commented` (a review body with no verdict)\n // is how a reviewer leaves notes without blocking — only a verdict that asks\n // for changes should pull the author back in.\n if (context.review.state.toLowerCase() !== 'changes_requested') return;\n // The authoring thread is already subscribed to this PR (`gh pr create`\n // subscribes automatically), so it can read the individual line comments\n // from its own notification inbox — the message only has to wake it and\n // point at the review.\n return {\n type: 'sendMessage',\n idempotencyKey: `${context.ingress.id}:address-review-feedback`,\n role: 'work',\n priority: 'high',\n message:\n `Changes were requested on pull request #${context.pullRequest.number} (${context.review.url}). ` +\n 'Read the review comments on this PR, address the ones you agree with, and push the fixes to the PR branch. ' +\n 'Reply on GitHub to anything you are deliberately not changing, explaining why.',\n } as const;\n}\n\n/**\n * Detects the `factory-review` handoff verdict in a comment body.\n *\n * GitHub forbids an app from reviewing a pull request it authored, so on\n * Factory-authored PRs the review skill falls back to posting its verdict as a\n * plain comment. That comment is the only signal the authoring agent gets, so\n * it has to be readable back out. The skill's handoff contract puts the verdict\n * on the first line (`Verdict: request changes`), so only that line is\n * inspected — a verdict quoted later in the findings must not count.\n */\nfunction requestsChangesVerdict(body: string | undefined): boolean {\n const firstLine = body\n ?.split('\\n')\n .map(line => line.trim())\n .find(line => line.length > 0);\n if (!firstLine) return false;\n // Tolerate the markdown the skill wraps the line in (`**Verdict: ...**`).\n const normalized = firstLine\n .replaceAll(/[*_`#>\\s]+/g, ' ')\n .trim()\n .toLowerCase();\n // Match the verdict exactly so negated phrasings (\"Verdict: do not request\n // changes\") cannot wake the author.\n return /^verdict: ?(request changes|changes requested)$/.test(normalized);\n}\n\nfunction addressPullRequestComment(context: FactoryGithubRuleContext) {\n if (!context.item || !context.pullRequest || !context.issueComment) return;\n // Provenance binds the comment to the Work item that authored the PR — the\n // only session that can act on it. A Review card must not react to comments\n // on the PR it is reviewing.\n if (context.board !== 'work') return;\n if (context.pullRequest.state !== 'open' || context.pullRequest.merged) return;\n // `factoryAuthored` is one bit for the whole Factory, so Factory's own\n // comments are indistinguishable between roles — waking on all of them would\n // let the Work agent's own progress comments wake itself in a loop. The one\n // exception is the review verdict the Review run had to post as a comment\n // because GitHub refused a self-review: that is the handoff, and it only ever\n // asks for changes once per review, so it cannot sustain a loop.\n if (\n context.actor.type === 'github' &&\n context.actor.factoryAuthored &&\n !requestsChangesVerdict(context.issueComment.body)\n ) {\n return;\n }\n return {\n type: 'sendMessage',\n idempotencyKey: `${context.ingress.id}:address-pull-request-comment`,\n role: 'work',\n priority: 'high',\n message:\n `${context.issueComment.author ?? 'Someone'} commented on pull request #${context.pullRequest.number} ` +\n `(${context.issueComment.url ?? context.pullRequest.url}). Read the comment, address it if you agree, and push ` +\n 'the fixes to the PR branch. Reply on GitHub to anything you are deliberately not changing, explaining why.',\n } as const;\n}\n\nfunction pullRequestClosed(context: FactoryGithubRuleContext) {\n if (!context.item || !context.pullRequest || context.pullRequest.merged) return;\n if (context.board !== 'review') return;\n // A PR closed without merging is abandoned review work: clear the card off\n // the board instead of leaving it in Reviewing forever.\n return {\n type: 'transition',\n idempotencyKey: `${context.ingress.id}:pull-request-closed`,\n board: 'review',\n stage: 'canceled',\n message: {\n text:\n `Pull request #${context.pullRequest.number} was closed without merging; ` +\n 'this Review card was moved to Canceled.',\n },\n } as const;\n}\n\nfunction reReviewRequestedPullRequest(context: FactoryGithubRuleContext) {\n // Only a review re-requested *from Factory's own bot* restarts the review —\n // requesting a human reviewer is not Factory's signal.\n if (!context.item || context.board !== 'review' || !context.reviewRequest?.factoryReviewer) return;\n if (!context.pullRequest || context.pullRequest.state !== 'open' || context.pullRequest.merged) return;\n // Trusted (write/admin) requesters only: re-entering review checks out and\n // executes PR code, the same bar pullRequestOpened applies to auto-review.\n if (!trustedGithubActor(context)) return;\n if (context.actor.type === 'github' && context.actor.factoryAuthored) return;\n // Already in Reviewing: a review pass is pending or running; re-entering\n // would be a same-stage no-op anyway (stage rules only fire on change).\n if (context.item.stages.length === 1 && context.item.stages[0] === 'review') return;\n return {\n type: 'transition',\n idempotencyKey: `${context.ingress.id}:re-review-requested`,\n board: 'review',\n stage: 'review',\n } as const;\n}\n\nfunction reReviewUpdatedPullRequest(context: FactoryGithubRuleContext) {\n if (!context.item || context.board !== 'review') return;\n if (!context.pullRequest || context.pullRequest.state !== 'open' || context.pullRequest.merged) return;\n // Intake has not started a review pass yet, so a push there is just more of\n // the code the first pass will read. A push to a card sitting in Reviewing is\n // different: it invalidates whatever that pass is reading, so re-enter the\n // stage to supersede it. `reviewPullRequest` cancels the stale run and picks\n // the right skill for the entry it sees.\n if (context.item.stages.some(stage => stage === 'intake')) return;\n const alreadyReviewing = context.item.stages.some(stage => stage === 'review');\n return {\n type: 'transition',\n idempotencyKey: `${context.ingress.id}:re-review-updated`,\n board: 'review',\n stage: 'review',\n // Re-entry is the point when the card is already Reviewing: the stage's\n // entry rule is what cancels the superseded pass and starts one on the code\n // that just landed.\n ...(alreadyReviewing ? { reenter: true } : {}),\n } as const;\n}\n\nfunction linearIssueObserved(context: FactoryLinearRuleContext) {\n if (context.item) return;\n return {\n type: 'upsertLinkedWorkItem',\n idempotencyKey: `${context.ingress.id}:issue-triage`,\n board: 'work',\n source: 'linear-issue',\n sourceKey: `linear:${context.issue.identifier}`,\n title: `${context.issue.identifier}: ${context.issue.title}`,\n url: context.issue.url,\n stage: 'triage',\n metadata: {\n linearIssueId: context.issue.id,\n identifier: context.issue.identifier,\n linearState: context.issue.state,\n linearStateType: context.issue.stateType,\n linearPriority: context.issue.priorityLabel,\n linearAssignee: context.issue.assignee,\n linearCreator: context.issue.creator,\n linearTeam: context.issue.team,\n labels: [...context.issue.labels] as string[],\n ...(context.issue.assignee ? { assignee: context.issue.assignee } : {}),\n ...(context.issue.creator ? { creator: context.issue.creator, author: context.issue.creator } : {}),\n },\n } as const;\n}\n\nfunction linearIssueClosed(context: FactoryLinearRuleContext) {\n if (!context.item || context.item.source !== 'linear-issue') return;\n if (context.board !== 'work') return;\n // Already off the board: nothing to reconcile.\n if (context.item.stages.some(stage => stage === 'done' || stage === 'canceled')) return;\n // Only terminal state types trigger close.\n const stateType = context.issue.stateType;\n if (stateType !== 'completed' && stateType !== 'canceled') return;\n const canceled = stateType === 'canceled';\n return {\n type: 'transition',\n idempotencyKey: `${context.ingress.id}:issue-closed`,\n board: 'work',\n stage: canceled ? 'canceled' : 'done',\n message: {\n text: `Linear issue ${context.issue.identifier} was ${canceled ? 'canceled' : 'completed'}; this Work card was moved to ${canceled ? 'Canceled' : 'Done'}.`,\n },\n } as const;\n}\n\nconst BUILT_IN_DEFAULTS: FactoryRulesOverrides = {\n work: {\n triage: {\n issue: { onEnter: investigateTriagedIssue },\n linearIssue: { onEnter: investigateTriagedLinearIssue },\n },\n planning: {\n issue: { onEnter: planWorkItem },\n linearIssue: { onEnter: planWorkItem },\n manual: { onEnter: planWorkItem },\n },\n execute: {\n issue: { onEnter: buildWorkItem },\n linearIssue: { onEnter: buildWorkItem },\n manual: { onEnter: buildWorkItem },\n },\n done: {\n issue: { onEnter: completeIssue },\n },\n },\n review: { review: { pullRequest: { onEnter: reviewPullRequest } } },\n tools: { submit_plan: { onResult: advanceApprovedPlan } },\n github: {\n issueOpened: { onEvent: issueOpened },\n issueEdited: { onEvent: retriageGithubIssue },\n issueClosed: { onEvent: issueClosed },\n issueCommentCreated: { onEvent: retriageGithubIssue },\n issueCommentEdited: { onEvent: retriageGithubIssue },\n issueCommentDeleted: { onEvent: retriageGithubIssue },\n pullRequestOpened: { onEvent: pullRequestOpened },\n pullRequestUpdated: { onEvent: reReviewUpdatedPullRequest },\n pullRequestCommentCreated: { onEvent: addressPullRequestComment },\n pullRequestReviewRequested: { onEvent: reReviewRequestedPullRequest },\n pullRequestReviewSubmitted: { onEvent: addressReviewFeedback },\n pullRequestMerged: { onEvent: pullRequestMerged },\n pullRequestClosed: { onEvent: pullRequestClosed },\n },\n linear: { issueObserved: { onEvent: linearIssueObserved }, issueClosed: { onEvent: linearIssueClosed } },\n};\n\nfunction mergeBoardRules(\n base: FactoryBoardRules | undefined,\n overrides: FactoryBoardRules | undefined,\n): FactoryBoardRules {\n const result: FactoryBoardRules = {};\n const stages = new Set([...Object.keys(base ?? {}), ...Object.keys(overrides ?? {})]) as Set<FactoryRuleStage>;\n for (const stage of stages) {\n const baseSources = base?.[stage];\n const overrideSources = overrides?.[stage];\n const sources = new Set([\n ...Object.keys(baseSources ?? {}),\n ...Object.keys(overrideSources ?? {}),\n ]) as Set<FactoryRuleSource>;\n const mergedSources: Partial<Record<FactoryRuleSource, FactoryBoardRuleLeaf>> = {};\n for (const source of sources) {\n const baseLeaf = baseSources?.[source];\n const overrideLeaf = overrideSources?.[source];\n mergedSources[source] = {\n ...(baseLeaf && 'onEnter' in baseLeaf ? { onEnter: baseLeaf.onEnter } : {}),\n ...(baseLeaf && 'onExit' in baseLeaf ? { onExit: baseLeaf.onExit } : {}),\n ...(overrideLeaf && 'onEnter' in overrideLeaf ? { onEnter: overrideLeaf.onEnter } : {}),\n ...(overrideLeaf && 'onExit' in overrideLeaf ? { onExit: overrideLeaf.onExit } : {}),\n };\n }\n result[stage] = mergedSources;\n }\n return result;\n}\n\nfunction mergeToolRules(\n base: Record<string, FactoryToolRuleLeaf> | undefined,\n overrides: Record<string, FactoryToolRuleLeaf> | undefined,\n): Record<string, FactoryToolRuleLeaf> {\n const result: Record<string, FactoryToolRuleLeaf> = {};\n for (const name of new Set([...Object.keys(base ?? {}), ...Object.keys(overrides ?? {})])) {\n const baseLeaf = base?.[name];\n const overrideLeaf = overrides?.[name];\n result[name] = {\n ...(baseLeaf && 'onResult' in baseLeaf ? { onResult: baseLeaf.onResult } : {}),\n ...(overrideLeaf && 'onResult' in overrideLeaf ? { onResult: overrideLeaf.onResult } : {}),\n };\n }\n return result;\n}\n\nfunction mergeGithubRules(\n base: FactoryRulesOverrides['github'],\n overrides: FactoryRulesOverrides['github'],\n): NonNullable<FactoryRulesOverrides['github']> {\n const result: Partial<Record<FactoryGithubEventName, FactoryGithubRuleLeaf>> = {};\n const events = new Set([...Object.keys(base ?? {}), ...Object.keys(overrides ?? {})]) as Set<FactoryGithubEventName>;\n for (const event of events) {\n const baseLeaf = base?.[event];\n const overrideLeaf = overrides?.[event];\n result[event] = {\n ...(baseLeaf && 'onEvent' in baseLeaf ? { onEvent: baseLeaf.onEvent } : {}),\n ...(overrideLeaf && 'onEvent' in overrideLeaf ? { onEvent: overrideLeaf.onEvent } : {}),\n };\n }\n return result;\n}\n\nfunction mergeLinearRules(\n base: FactoryRulesOverrides['linear'],\n overrides: FactoryRulesOverrides['linear'],\n): NonNullable<FactoryRulesOverrides['linear']> {\n const result: Partial<Record<FactoryLinearEventName, FactoryLinearRuleLeaf>> = {};\n const events = new Set([...Object.keys(base ?? {}), ...Object.keys(overrides ?? {})]) as Set<FactoryLinearEventName>;\n for (const event of events) {\n const baseLeaf = base?.[event];\n const overrideLeaf = overrides?.[event];\n result[event] = {\n ...(baseLeaf && 'onEvent' in baseLeaf ? { onEvent: baseLeaf.onEvent } : {}),\n ...(overrideLeaf && 'onEvent' in overrideLeaf ? { onEvent: overrideLeaf.onEvent } : {}),\n };\n }\n return result;\n}\n\nexport function mergeFactoryRuleOverrides(\n base: FactoryRulesOverrides,\n overrides: FactoryRulesOverrides = {},\n): Omit<FactoryRules, 'version'> {\n return {\n work: mergeBoardRules(base.work, overrides.work),\n review: mergeBoardRules(base.review, overrides.review),\n tools: mergeToolRules(base.tools, overrides.tools),\n github: mergeGithubRules(base.github, overrides.github),\n linear: mergeLinearRules(base.linear, overrides.linear),\n };\n}\n\nexport function defaultFactoryRules(input: { version: string; overrides?: FactoryRulesOverrides }): FactoryRules {\n if (typeof input?.version !== 'string' || input.version.trim().length === 0) {\n throw new FactoryRuleValidationError('Factory rule version is required.');\n }\n\n const rules: FactoryRules = {\n version: input.version.trim(),\n ...mergeFactoryRuleOverrides(BUILT_IN_DEFAULTS, input.overrides),\n };\n assertFactoryRules(rules);\n return rules;\n}\n\nexport function builtInFactoryRules(): FactoryRules {\n return defaultFactoryRules({ version: DEFAULT_FACTORY_RULE_VERSION });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"defaults.js","names":[],"sources":["../../src/rules/defaults.ts"],"sourcesContent":["import type {\n FactoryBoardRuleLeaf,\n FactoryBoardRules,\n FactoryGithubRuleLeaf,\n FactoryGithubEventName,\n FactoryGithubRuleContext,\n FactoryLinearEventName,\n FactoryLinearRuleContext,\n FactoryLinearRuleLeaf,\n FactoryRules,\n FactoryRulesOverrides,\n FactoryRuleSource,\n FactoryRuleStage,\n FactoryStageRuleContext,\n FactoryToolResultRuleContext,\n FactoryToolRuleLeaf,\n} from './types.js';\nimport { assertFactoryRules, FactoryRuleValidationError } from './validation.js';\n\nexport const DEFAULT_FACTORY_RULE_VERSION = 'factory-default-v1';\n\nfunction trustedGithubActor(context: Pick<FactoryStageRuleContext, 'actor'>): boolean {\n return context.actor.type === 'github' && context.actor.trusted;\n}\n\nfunction githubActorLogin(context: Pick<FactoryStageRuleContext, 'actor'>): string | undefined {\n return context.actor.type === 'github' ? context.actor.login : undefined;\n}\n\nfunction invokeIssueInvestigation(context: FactoryStageRuleContext) {\n return {\n type: 'invokeSkill',\n idempotencyKey: `${context.ingress.id}:factory-triage`,\n role: 'triage',\n skillName: 'factory-triage',\n arguments: context.item.url ? `GitHub issue (${context.item.url})` : context.item.title,\n } as const;\n}\n\nfunction retriageGithubIssue(context: FactoryGithubRuleContext) {\n if (!context.item || context.item.source !== 'github-issue' || !context.item.url) return;\n if (context.actor.type === 'github' && context.actor.factoryAuthored) return;\n\n const reason =\n context.event === 'issueEdited'\n ? context.issueChange?.title && context.issueChange.body\n ? 'issue title and body edited'\n : context.issueChange?.title\n ? 'issue title edited'\n : 'issue body edited'\n : context.event === 'issueCommentDeleted'\n ? 'comment deleted'\n : context.event === 'issueCommentEdited'\n ? 'comment edited'\n : 'comment created';\n return {\n type: 'invokeSkill',\n idempotencyKey: `${context.ingress.id}:factory-triage`,\n role: 'triage',\n skillName: 'factory-triage',\n arguments: `Re-triage GitHub issue (${context.item.url}) after ${reason}.`,\n } as const;\n}\n\nfunction investigateTriagedLinearIssue(context: FactoryStageRuleContext) {\n const identifier = context.item.sourceKey?.startsWith('linear:')\n ? context.item.sourceKey.slice('linear:'.length)\n : context.item.title;\n return {\n type: 'invokeSkill',\n idempotencyKey: `${context.ingress.id}:factory-triage-linear`,\n role: 'triage',\n skillName: 'factory-triage',\n arguments: `Linear issue ${identifier}${context.item.url ? ` (${context.item.url})` : ''}`,\n } as const;\n}\n\nfunction planWorkItem(context: FactoryStageRuleContext) {\n return {\n type: 'invokeSkill',\n idempotencyKey: `${context.ingress.id}:factory-plan`,\n role: 'plan',\n skillName: 'factory-plan',\n arguments: context.item.url ? `Work item (${context.item.url})` : context.item.title,\n } as const;\n}\n\n// A GitHub login is alphanumeric with interior hyphens — no underscores, no\n// spaces. Checking the grammar rejects the placeholder the issue poller stamps\n// when the reporter's account is gone (`__unknown__`), which would otherwise\n// become a trailer crediting an account that does not exist.\nconst GITHUB_LOGIN = /^[a-zA-Z0-9](?:[a-zA-Z0-9]|-(?=[a-zA-Z0-9])){0,38}$/;\n\n/**\n * The reporter earns a `Co-Authored-By` trailer on the work their report caused.\n * Only a GitHub issue qualifies: Linear stamps a display name and a manual card\n * stamps nothing, and neither resolves to the GitHub identity a trailer needs.\n * Factory's own reports are skipped — crediting ourselves is noise.\n */\nfunction reporterCoAuthor(context: FactoryStageRuleContext) {\n if (context.source !== 'issue') return undefined;\n const author = context.item.metadata?.author;\n if (typeof author !== 'string' || !author) return undefined;\n if (author.endsWith('[bot]') || !GITHUB_LOGIN.test(author)) return undefined;\n return author;\n}\n\n/**\n * Building carries a prompt rather than a skill. The approved plan is already\n * the specification, so there is nothing for a skill document to add, and the\n * handoff a skill would define is unnecessary here: Building ends by opening a\n * pull request, which arrives as its own event and raises the Review card.\n */\nfunction buildWorkItem(context: FactoryStageRuleContext) {\n const subject = context.item.url ? `the approved plan for ${context.item.url}` : 'the approved plan';\n const reporter = reporterCoAuthor(context);\n // The trailer needs the reporter's numeric id, which intake does not stamp, so\n // the agent resolves it from the same issue it is already reading.\n const credit = reporter\n ? ` The work was reported by @${reporter}: credit them on every commit with a ` +\n `\\`Co-Authored-By: ${reporter} <ID+${reporter}@users.noreply.github.com>\\` trailer, ` +\n `resolving ID with \\`gh api users/${reporter} --jq .id\\`.`\n : '';\n return {\n type: 'invokeSkill',\n idempotencyKey: `${context.ingress.id}:build`,\n role: 'work',\n prompt: `Implement ${subject}. Open a pull request when the work is ready for review.${credit}`,\n } as const;\n}\n\nfunction completeIssue(context: FactoryStageRuleContext) {\n return {\n type: 'invokeSkill',\n idempotencyKey: `${context.ingress.id}:factory-complete-issue`,\n role: 'triage',\n skillName: 'factory-complete-issue',\n arguments: context.item.url ? `GitHub issue (${context.item.url})` : context.item.title,\n } as const;\n}\n\nfunction reviewPullRequest(context: FactoryStageRuleContext) {\n // A re-entry into Review (from any post-intake stage) supersedes whichever\n // review pass previously ran on this card: cancel any in-flight run before\n // dispatching a fresh one so we don't burn tokens on the stale pass and race\n // two agents on the same card. Cancellation is safe when nothing is in flight.\n const supersedes = context.fromStage !== 'intake';\n // The re-review skill only applies when a prior review pass actually completed\n // (the card is returning from `done`). A cancelled first-time review that\n // re-enters Review from `review` itself still has no prior pass to reconcile —\n // it gets the regular factory-review skill.\n const priorReviewCompleted = context.fromStage === 'done';\n const skillName = priorReviewCompleted ? 'factory-rereview' : 'factory-review';\n return {\n type: 'invokeSkill',\n idempotencyKey: `${context.ingress.id}:${skillName}`,\n role: 'review',\n skillName,\n arguments: context.item.url ? `GitHub pull request (${context.item.url})` : context.item.title,\n ...(supersedes ? { cancelInFlight: true } : {}),\n } as const;\n}\n\n// Fires only on webhook materialization, so an item filed by hand or re-synced\n// from source never suggests its own run.\nfunction onArrival<Effect>(rule: (context: FactoryStageRuleContext) => Effect) {\n return (context: FactoryStageRuleContext): Effect | undefined => {\n if (context.cause !== 'linked_item_materialized') return;\n if (context.item.metadata?.autoStartCandidate !== true) return;\n return rule(context);\n };\n}\n\nfunction resultContent(value: unknown): string | undefined {\n if (typeof value === 'string') return value;\n if (!value || typeof value !== 'object' || Array.isArray(value)) return;\n const content = (value as { content?: unknown }).content;\n return typeof content === 'string' ? content : undefined;\n}\n\n// Interactive-session path only: factory-plan never calls submit_plan — it\n// advances planning → execute via factory_transition_work_item directly.\nfunction advanceApprovedPlan(context: FactoryToolResultRuleContext) {\n if (\n context.result.status !== 'success' ||\n context.board !== 'work' ||\n context.item.stages.length !== 1 ||\n context.item.stages[0] !== 'planning' ||\n context.actor.type !== 'agent' ||\n context.actor.role !== 'plan' ||\n !resultContent(context.result.value)?.startsWith('Plan approved.')\n ) {\n return;\n }\n return {\n type: 'transition',\n idempotencyKey: `${context.ingress.id}:approved-plan`,\n board: 'work',\n stage: 'execute',\n } as const;\n}\n\nfunction createdAfterFactory(createdAt: string | undefined, factoryCreatedAt: string): boolean {\n if (!createdAt) return false;\n const sourceCreatedAt = Date.parse(createdAt);\n const projectCreatedAt = Date.parse(factoryCreatedAt);\n return Number.isFinite(sourceCreatedAt) && Number.isFinite(projectCreatedAt) && sourceCreatedAt > projectCreatedAt;\n}\n\nfunction issueOpened(context: FactoryGithubRuleContext) {\n if (!context.issue) return;\n // Everything arrives in Intake; arrival only stamps whether `onArrival` may\n // suggest this card's run without a person.\n return {\n type: 'upsertLinkedWorkItem',\n idempotencyKey: `${context.ingress.id}:issue-intake`,\n board: 'work',\n source: 'github-issue',\n sourceKey: `github-issue:${context.issue.number}`,\n title: context.issue.title,\n url: context.issue.url,\n stage: 'intake',\n metadata: {\n githubRepositoryId: context.repository.id,\n githubIssueNumber: context.issue.number,\n ...(githubActorLogin(context) ? { author: githubActorLogin(context) } : {}),\n authorTrusted: trustedGithubActor(context),\n autoStartCandidate:\n trustedGithubActor(context) && createdAfterFactory(context.issue.createdAt, context.factory.createdAt),\n assignees: context.issue.assignees ?? [],\n labels: context.issue.labels ?? [],\n },\n } as const;\n}\n\nfunction issueClosed(context: FactoryGithubRuleContext) {\n if (!context.item || context.item.source !== 'github-issue' || !context.issue) return;\n if (context.board !== 'work') return;\n // Already off the board: nothing to reconcile.\n if (context.item.stages.some(stage => stage === 'done' || stage === 'canceled')) return;\n // Issue closure is a repository fact, not third-party input — no actor trust\n // gate. `not_planned` (and `duplicate`) means abandoned, everything else is\n // completed work.\n const canceled = context.issue.stateReason === 'not_planned' || context.issue.stateReason === 'duplicate';\n return {\n type: 'transition',\n idempotencyKey: `${context.ingress.id}:issue-closed`,\n board: 'work',\n stage: canceled ? 'canceled' : 'done',\n message: {\n text:\n `GitHub issue #${context.issue.number} was closed` +\n `${context.issue.stateReason ? ` (${context.issue.stateReason})` : ''}; ` +\n `this Work card was moved to ${canceled ? 'Canceled' : 'Done'}.`,\n },\n } as const;\n}\n\nfunction pullRequestOpened(context: FactoryGithubRuleContext) {\n if (!context.pullRequest) return;\n // A GitHub App bot is never a collaborator, so Factory's own PRs score\n // untrusted; their authorship is the trust signal.\n const factoryAuthored = context.actor.type === 'github' && context.actor.factoryAuthored;\n const autoStartCandidate =\n (trustedGithubActor(context) || factoryAuthored) &&\n createdAfterFactory(context.pullRequest.createdAt, context.factory.createdAt);\n return {\n type: 'upsertLinkedWorkItem',\n idempotencyKey: `${context.ingress.id}:pull-request-intake`,\n board: 'review',\n source: 'github-pr',\n sourceKey: `github-pr:${context.pullRequest.number}`,\n title: context.pullRequest.title,\n url: context.pullRequest.url,\n stage: 'intake',\n metadata: {\n githubRepositoryId: context.repository.id,\n githubPullRequestNumber: context.pullRequest.number,\n factoryAuthored,\n authorTrusted: trustedGithubActor(context),\n autoStartCandidate,\n state: context.pullRequest.state,\n draft: context.pullRequest.draft,\n merged: context.pullRequest.merged,\n assignees: context.pullRequest.assignees ?? [],\n requestedReviewers: context.pullRequest.requestedReviewers ?? [],\n labels: context.pullRequest.labels ?? [],\n headBranch: context.pullRequest.headBranch,\n baseBranch: context.pullRequest.baseBranch,\n ...(githubActorLogin(context) ? { author: githubActorLogin(context) } : {}),\n },\n } as const;\n}\n\nfunction pullRequestMerged(context: FactoryGithubRuleContext) {\n if (!context.item || !context.pullRequest?.merged) return;\n if (context.board === 'review') {\n // The event is bound to the PR's own Review card: a merged PR is finished\n // review work, so always move the card to Done. The message only reaches\n // an active session (if any) — cards without one just move, instead of\n // failing retries against a binding that never existed.\n return {\n type: 'transition',\n idempotencyKey: `${context.ingress.id}:pull-request-merged`,\n board: 'review',\n stage: 'done',\n message: {\n text:\n `Pull request #${context.pullRequest.number} merged; this Review card was moved to Done. ` +\n 'No further review is needed unless follow-up work was requested.',\n },\n } as const;\n }\n // Provenance bound the event to the originating Work item instead: remind\n // its agent to assess completion — never auto-complete the Work item.\n return {\n type: 'sendMessage',\n idempotencyKey: `${context.ingress.id}:assess-work-completion`,\n role: 'work',\n message:\n `Pull request #${context.pullRequest.number} merged. Assess whether the linked Work item is complete. ` +\n 'Do not mark it Done solely because this PR merged; use factory_transition_work_item only after verifying the work.',\n } as const;\n}\n\nfunction addressReviewFeedback(context: FactoryGithubRuleContext) {\n if (!context.item || !context.pullRequest || !context.review) return;\n // Only the Work item that authored the PR can act on the feedback. Provenance\n // binds the event there; a Review card seeing its own posted review must not\n // react to it (that would loop the reviewer against itself).\n if (context.board !== 'work') return;\n // A closed or merged pull request has no branch left to push fixes to.\n if (context.pullRequest.state !== 'open' || context.pullRequest.merged) return;\n // `approved` needs no work, and `commented` (a review body with no verdict)\n // is how a reviewer leaves notes without blocking — only a verdict that asks\n // for changes should pull the author back in.\n if (context.review.state.toLowerCase() !== 'changes_requested') return;\n // The authoring thread is already subscribed to this PR (`gh pr create`\n // subscribes automatically), so it can read the individual line comments\n // from its own notification inbox — the message only has to wake it and\n // point at the review.\n return {\n type: 'sendMessage',\n idempotencyKey: `${context.ingress.id}:address-review-feedback`,\n role: 'work',\n priority: 'high',\n message:\n `Changes were requested on pull request #${context.pullRequest.number} (${context.review.url}). ` +\n 'Read the review comments on this PR, address the ones you agree with, and push the fixes to the PR branch. ' +\n 'Reply on GitHub to anything you are deliberately not changing, explaining why.',\n } as const;\n}\n\n/**\n * Detects the `factory-review` handoff verdict in a comment body.\n *\n * GitHub forbids an app from reviewing a pull request it authored, so on\n * Factory-authored PRs the review skill falls back to posting its verdict as a\n * plain comment. That comment is the only signal the authoring agent gets, so\n * it has to be readable back out. The skill's handoff contract puts the verdict\n * on the first line (`Verdict: request changes`), so only that line is\n * inspected — a verdict quoted later in the findings must not count.\n */\nfunction requestsChangesVerdict(body: string | undefined): boolean {\n const firstLine = body\n ?.split('\\n')\n .map(line => line.trim())\n .find(line => line.length > 0);\n if (!firstLine) return false;\n // Tolerate the markdown the skill wraps the line in (`**Verdict: ...**`).\n const normalized = firstLine\n .replaceAll(/[*_`#>\\s]+/g, ' ')\n .trim()\n .toLowerCase();\n // Match the verdict exactly so negated phrasings (\"Verdict: do not request\n // changes\") cannot wake the author.\n return /^verdict: ?(request changes|changes requested)$/.test(normalized);\n}\n\nfunction addressPullRequestComment(context: FactoryGithubRuleContext) {\n if (!context.item || !context.pullRequest || !context.issueComment) return;\n // Provenance binds the comment to the Work item that authored the PR — the\n // only session that can act on it. A Review card must not react to comments\n // on the PR it is reviewing.\n if (context.board !== 'work') return;\n if (context.pullRequest.state !== 'open' || context.pullRequest.merged) return;\n // `factoryAuthored` is one bit for the whole Factory, so Factory's own\n // comments are indistinguishable between roles — waking on all of them would\n // let the Work agent's own progress comments wake itself in a loop. The one\n // exception is the review verdict the Review run had to post as a comment\n // because GitHub refused a self-review: that is the handoff, and it only ever\n // asks for changes once per review, so it cannot sustain a loop.\n if (\n context.actor.type === 'github' &&\n context.actor.factoryAuthored &&\n !requestsChangesVerdict(context.issueComment.body)\n ) {\n return;\n }\n return {\n type: 'sendMessage',\n idempotencyKey: `${context.ingress.id}:address-pull-request-comment`,\n role: 'work',\n priority: 'high',\n message:\n `${context.issueComment.author ?? 'Someone'} commented on pull request #${context.pullRequest.number} ` +\n `(${context.issueComment.url ?? context.pullRequest.url}). Read the comment, address it if you agree, and push ` +\n 'the fixes to the PR branch. Reply on GitHub to anything you are deliberately not changing, explaining why.',\n } as const;\n}\n\nfunction pullRequestClosed(context: FactoryGithubRuleContext) {\n if (!context.item || !context.pullRequest || context.pullRequest.merged) return;\n if (context.board !== 'review') return;\n // A PR closed without merging is abandoned review work: clear the card off\n // the board instead of leaving it in Reviewing forever.\n return {\n type: 'transition',\n idempotencyKey: `${context.ingress.id}:pull-request-closed`,\n board: 'review',\n stage: 'canceled',\n message: {\n text:\n `Pull request #${context.pullRequest.number} was closed without merging; ` +\n 'this Review card was moved to Canceled.',\n },\n } as const;\n}\n\nfunction reReviewRequestedPullRequest(context: FactoryGithubRuleContext) {\n // Only a review re-requested *from Factory's own bot* restarts the review —\n // requesting a human reviewer is not Factory's signal.\n if (!context.item || context.board !== 'review' || !context.reviewRequest?.factoryReviewer) return;\n if (!context.pullRequest || context.pullRequest.state !== 'open' || context.pullRequest.merged) return;\n // Trusted (write/admin) requesters only: re-entering review checks out and\n // executes PR code, the same bar pullRequestOpened applies to auto-review.\n if (!trustedGithubActor(context)) return;\n if (context.actor.type === 'github' && context.actor.factoryAuthored) return;\n // Already in Reviewing: a review pass is pending or running; re-entering\n // would be a same-stage no-op anyway (stage rules only fire on change).\n if (context.item.stages.length === 1 && context.item.stages[0] === 'review') return;\n return {\n type: 'transition',\n idempotencyKey: `${context.ingress.id}:re-review-requested`,\n board: 'review',\n stage: 'review',\n } as const;\n}\n\nfunction reReviewUpdatedPullRequest(context: FactoryGithubRuleContext) {\n if (!context.item || context.board !== 'review') return;\n if (!context.pullRequest || context.pullRequest.state !== 'open' || context.pullRequest.merged) return;\n // Intake has not started a review pass yet, so a push there is just more of\n // the code the first pass will read. A push to a card sitting in Reviewing is\n // different: it invalidates whatever that pass is reading, so re-enter the\n // stage to supersede it. `reviewPullRequest` cancels the stale run and picks\n // the right skill for the entry it sees.\n if (context.item.stages.some(stage => stage === 'intake')) return;\n const alreadyReviewing = context.item.stages.some(stage => stage === 'review');\n return {\n type: 'transition',\n idempotencyKey: `${context.ingress.id}:re-review-updated`,\n board: 'review',\n stage: 'review',\n // Re-entry is the point when the card is already Reviewing: the stage's\n // entry rule is what cancels the superseded pass and starts one on the code\n // that just landed.\n ...(alreadyReviewing ? { reenter: true } : {}),\n } as const;\n}\n\nfunction linearIssueObserved(context: FactoryLinearRuleContext) {\n if (context.item) return;\n return {\n type: 'upsertLinkedWorkItem',\n idempotencyKey: `${context.ingress.id}:issue-triage`,\n board: 'work',\n source: 'linear-issue',\n sourceKey: `linear:${context.issue.identifier}`,\n title: `${context.issue.identifier}: ${context.issue.title}`,\n url: context.issue.url,\n stage: 'triage',\n metadata: {\n linearIssueId: context.issue.id,\n identifier: context.issue.identifier,\n linearState: context.issue.state,\n linearStateType: context.issue.stateType,\n linearPriority: context.issue.priorityLabel,\n linearAssignee: context.issue.assignee,\n linearCreator: context.issue.creator,\n linearTeam: context.issue.team,\n labels: [...context.issue.labels] as string[],\n ...(context.issue.assignee ? { assignee: context.issue.assignee } : {}),\n ...(context.issue.creator ? { creator: context.issue.creator, author: context.issue.creator } : {}),\n },\n } as const;\n}\n\nfunction linearIssueClosed(context: FactoryLinearRuleContext) {\n if (!context.item || context.item.source !== 'linear-issue') return;\n if (context.board !== 'work') return;\n // Already off the board: nothing to reconcile.\n if (context.item.stages.some(stage => stage === 'done' || stage === 'canceled')) return;\n // Only terminal state types trigger close.\n const stateType = context.issue.stateType;\n if (stateType !== 'completed' && stateType !== 'canceled') return;\n const canceled = stateType === 'canceled';\n return {\n type: 'transition',\n idempotencyKey: `${context.ingress.id}:issue-closed`,\n board: 'work',\n stage: canceled ? 'canceled' : 'done',\n message: {\n text: `Linear issue ${context.issue.identifier} was ${canceled ? 'canceled' : 'completed'}; this Work card was moved to ${canceled ? 'Canceled' : 'Done'}.`,\n },\n } as const;\n}\n\nconst BUILT_IN_DEFAULTS: FactoryRulesOverrides = {\n work: {\n intake: { issue: { onEnter: onArrival(invokeIssueInvestigation) } },\n triage: {\n issue: { onEnter: invokeIssueInvestigation },\n linearIssue: { onEnter: investigateTriagedLinearIssue },\n },\n planning: {\n issue: { onEnter: planWorkItem },\n linearIssue: { onEnter: planWorkItem },\n manual: { onEnter: planWorkItem },\n },\n execute: {\n issue: { onEnter: buildWorkItem },\n linearIssue: { onEnter: buildWorkItem },\n manual: { onEnter: buildWorkItem },\n },\n done: {\n issue: { onEnter: completeIssue },\n },\n },\n review: {\n intake: { pullRequest: { onEnter: onArrival(reviewPullRequest) } },\n review: { pullRequest: { onEnter: reviewPullRequest } },\n },\n tools: { submit_plan: { onResult: advanceApprovedPlan } },\n github: {\n issueOpened: { onEvent: issueOpened },\n issueEdited: { onEvent: retriageGithubIssue },\n issueClosed: { onEvent: issueClosed },\n issueCommentCreated: { onEvent: retriageGithubIssue },\n issueCommentEdited: { onEvent: retriageGithubIssue },\n issueCommentDeleted: { onEvent: retriageGithubIssue },\n pullRequestOpened: { onEvent: pullRequestOpened },\n pullRequestUpdated: { onEvent: reReviewUpdatedPullRequest },\n pullRequestCommentCreated: { onEvent: addressPullRequestComment },\n pullRequestReviewRequested: { onEvent: reReviewRequestedPullRequest },\n pullRequestReviewSubmitted: { onEvent: addressReviewFeedback },\n pullRequestMerged: { onEvent: pullRequestMerged },\n pullRequestClosed: { onEvent: pullRequestClosed },\n },\n linear: { issueObserved: { onEvent: linearIssueObserved }, issueClosed: { onEvent: linearIssueClosed } },\n};\n\nfunction mergeBoardRules(\n base: FactoryBoardRules | undefined,\n overrides: FactoryBoardRules | undefined,\n): FactoryBoardRules {\n const result: FactoryBoardRules = {};\n const stages = new Set([...Object.keys(base ?? {}), ...Object.keys(overrides ?? {})]) as Set<FactoryRuleStage>;\n for (const stage of stages) {\n const baseSources = base?.[stage];\n const overrideSources = overrides?.[stage];\n const sources = new Set([\n ...Object.keys(baseSources ?? {}),\n ...Object.keys(overrideSources ?? {}),\n ]) as Set<FactoryRuleSource>;\n const mergedSources: Partial<Record<FactoryRuleSource, FactoryBoardRuleLeaf>> = {};\n for (const source of sources) {\n const baseLeaf = baseSources?.[source];\n const overrideLeaf = overrideSources?.[source];\n mergedSources[source] = {\n ...(baseLeaf && 'onEnter' in baseLeaf ? { onEnter: baseLeaf.onEnter } : {}),\n ...(baseLeaf && 'onExit' in baseLeaf ? { onExit: baseLeaf.onExit } : {}),\n ...(overrideLeaf && 'onEnter' in overrideLeaf ? { onEnter: overrideLeaf.onEnter } : {}),\n ...(overrideLeaf && 'onExit' in overrideLeaf ? { onExit: overrideLeaf.onExit } : {}),\n };\n }\n result[stage] = mergedSources;\n }\n return result;\n}\n\nfunction mergeToolRules(\n base: Record<string, FactoryToolRuleLeaf> | undefined,\n overrides: Record<string, FactoryToolRuleLeaf> | undefined,\n): Record<string, FactoryToolRuleLeaf> {\n const result: Record<string, FactoryToolRuleLeaf> = {};\n for (const name of new Set([...Object.keys(base ?? {}), ...Object.keys(overrides ?? {})])) {\n const baseLeaf = base?.[name];\n const overrideLeaf = overrides?.[name];\n result[name] = {\n ...(baseLeaf && 'onResult' in baseLeaf ? { onResult: baseLeaf.onResult } : {}),\n ...(overrideLeaf && 'onResult' in overrideLeaf ? { onResult: overrideLeaf.onResult } : {}),\n };\n }\n return result;\n}\n\nfunction mergeGithubRules(\n base: FactoryRulesOverrides['github'],\n overrides: FactoryRulesOverrides['github'],\n): NonNullable<FactoryRulesOverrides['github']> {\n const result: Partial<Record<FactoryGithubEventName, FactoryGithubRuleLeaf>> = {};\n const events = new Set([...Object.keys(base ?? {}), ...Object.keys(overrides ?? {})]) as Set<FactoryGithubEventName>;\n for (const event of events) {\n const baseLeaf = base?.[event];\n const overrideLeaf = overrides?.[event];\n result[event] = {\n ...(baseLeaf && 'onEvent' in baseLeaf ? { onEvent: baseLeaf.onEvent } : {}),\n ...(overrideLeaf && 'onEvent' in overrideLeaf ? { onEvent: overrideLeaf.onEvent } : {}),\n };\n }\n return result;\n}\n\nfunction mergeLinearRules(\n base: FactoryRulesOverrides['linear'],\n overrides: FactoryRulesOverrides['linear'],\n): NonNullable<FactoryRulesOverrides['linear']> {\n const result: Partial<Record<FactoryLinearEventName, FactoryLinearRuleLeaf>> = {};\n const events = new Set([...Object.keys(base ?? {}), ...Object.keys(overrides ?? {})]) as Set<FactoryLinearEventName>;\n for (const event of events) {\n const baseLeaf = base?.[event];\n const overrideLeaf = overrides?.[event];\n result[event] = {\n ...(baseLeaf && 'onEvent' in baseLeaf ? { onEvent: baseLeaf.onEvent } : {}),\n ...(overrideLeaf && 'onEvent' in overrideLeaf ? { onEvent: overrideLeaf.onEvent } : {}),\n };\n }\n return result;\n}\n\nexport function mergeFactoryRuleOverrides(\n base: FactoryRulesOverrides,\n overrides: FactoryRulesOverrides = {},\n): Omit<FactoryRules, 'version'> {\n return {\n work: mergeBoardRules(base.work, overrides.work),\n review: mergeBoardRules(base.review, overrides.review),\n tools: mergeToolRules(base.tools, overrides.tools),\n github: mergeGithubRules(base.github, overrides.github),\n linear: mergeLinearRules(base.linear, overrides.linear),\n };\n}\n\nexport function defaultFactoryRules(input: { version: string; overrides?: FactoryRulesOverrides }): FactoryRules {\n if (typeof input?.version !== 'string' || input.version.trim().length === 0) {\n throw new FactoryRuleValidationError('Factory rule version is required.');\n }\n\n const rules: FactoryRules = {\n version: input.version.trim(),\n ...mergeFactoryRuleOverrides(BUILT_IN_DEFAULTS, input.overrides),\n };\n assertFactoryRules(rules);\n return rules;\n}\n\nexport function builtInFactoryRules(): FactoryRules {\n return defaultFactoryRules({ version: DEFAULT_FACTORY_RULE_VERSION });\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"dispatcher.d.ts","sourceRoot":"","sources":["../../src/rules/dispatcher.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,KAAK,EAAE,eAAe,EAAyC,MAAM,+BAA+B,CAAC;AAM5G,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,6CAA6C,CAAC;AACrF,OAAO,KAAK,EACV,6BAA6B,EAG7B,WAAW,EACX,gBAAgB,EACjB,MAAM,uCAAuC,CAAC;AAG/C,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AACxE,OAAO,KAAK,EAAE,qBAAqB,EAA4C,MAAM,YAAY,CAAC;AA+DlG,KAAK,iBAAiB,GAAG,IAAI,CAAC,eAAe,CAAC,eAAe,CAAC,EAAE,sBAAsB,CAAC,CAAC;AAGxF,MAAM,WAAW,8BAA8B;IAC7C,MAAM,EAAE,6BAA6B,CAAC;IACtC,IAAI,EAAE,WAAW,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,gCAAgC;IAC/C,UAAU,EAAE,iBAAiB,CAAC;IAC9B,iBAAiB,EAAE,IAAI,CAAC,wBAAwB,EAAE,YAAY,CAAC,CAAC;IAChE,OAAO,EAAE,gBAAgB,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,wFAAwF;IACxF,gBAAgB,EAAE,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,gBAAgB,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5F,oBAAoB,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,8BAA8B,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1E,gBAAgB,CAAC,EAAE,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAChF,8EAA8E;IAC9E,UAAU,CAAC,EAAE,iBAAiB,CAAC;IAC/B,6BAA6B,CAAC,EAAE,CAAC,KAAK,EAAE;QACtC,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,OAAO,CAAC,qBAAqB,EAAE;YAAE,IAAI,EAAE,sBAAsB,CAAA;SAAE,CAAC,CAAC;KAC5E,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sEAAsE;IACtE,2BAA2B,CAAC,EAAE,MAAM,CAAC;IACrC,sFAAsF;IACtF,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,8EAA8E;IAC9E,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,oGAAoG;IACpG,mCAAmC,CAAC,EAAE,MAAM,CAAC;CAC9C;AA+FD,qBAAa,yBAAyB;;gBAuBxB,OAAO,EAAE,gCAAgC;IAyBrD,KAAK,IAAI,IAAI;IAOP,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAOrB,OAAO,CAAC,GAAG,OAAa,GAAG,OAAO,CAAC,IAAI,CAAC;CAyuB/C;AAED,eAAO,MAAM,0BAA0B;;;;;;;;;;CAU7B,CAAC"}
1
+ {"version":3,"file":"dispatcher.d.ts","sourceRoot":"","sources":["../../src/rules/dispatcher.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,KAAK,EAAE,eAAe,EAAyC,MAAM,+BAA+B,CAAC;AAM5G,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,6CAA6C,CAAC;AACrF,OAAO,KAAK,EACV,6BAA6B,EAG7B,WAAW,EACX,gBAAgB,EACjB,MAAM,uCAAuC,CAAC;AAG/C,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AACxE,OAAO,KAAK,EAAE,qBAAqB,EAA4C,MAAM,YAAY,CAAC;AA+DlG,KAAK,iBAAiB,GAAG,IAAI,CAAC,eAAe,CAAC,eAAe,CAAC,EAAE,sBAAsB,CAAC,CAAC;AAGxF,MAAM,WAAW,8BAA8B;IAC7C,MAAM,EAAE,6BAA6B,CAAC;IACtC,IAAI,EAAE,WAAW,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,gCAAgC;IAC/C,UAAU,EAAE,iBAAiB,CAAC;IAC9B,iBAAiB,EAAE,IAAI,CAAC,wBAAwB,EAAE,YAAY,CAAC,CAAC;IAChE,OAAO,EAAE,gBAAgB,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,wFAAwF;IACxF,gBAAgB,EAAE,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,gBAAgB,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5F,oBAAoB,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,8BAA8B,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1E,gBAAgB,CAAC,EAAE,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAChF,8EAA8E;IAC9E,UAAU,CAAC,EAAE,iBAAiB,CAAC;IAC/B,6BAA6B,CAAC,EAAE,CAAC,KAAK,EAAE;QACtC,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,OAAO,CAAC,qBAAqB,EAAE;YAAE,IAAI,EAAE,sBAAsB,CAAA;SAAE,CAAC,CAAC;KAC5E,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sEAAsE;IACtE,2BAA2B,CAAC,EAAE,MAAM,CAAC;IACrC,sFAAsF;IACtF,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,8EAA8E;IAC9E,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,oGAAoG;IACpG,mCAAmC,CAAC,EAAE,MAAM,CAAC;CAC9C;AAyGD,qBAAa,yBAAyB;;gBAuBxB,OAAO,EAAE,gCAAgC;IAyBrD,KAAK,IAAI,IAAI;IAOP,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAOrB,OAAO,CAAC,GAAG,OAAa,GAAG,OAAO,CAAC,IAAI,CAAC;CAuvB/C;AAED,eAAO,MAAM,0BAA0B;;;;;;;;;;CAU7B,CAAC"}
@@ -1,4 +1,4 @@
1
- import { FACTORY_RULE_STAGES } from "./types.js";
1
+ import { FACTORY_RULE_STAGES, externallyAuthoredWorkItem, isWorkingFactoryRuleStage } from "./types.js";
2
2
  import { validateFactoryRuleDecision } from "./validation.js";
3
3
  import { FACTORY_RULE_MATERIALIZATION_KEY } from "../storage/domains/work-items/base.js";
4
4
  import { FactoryDispatchError, factoryDispatchFailureCode } from "./dispatch-errors.js";
@@ -59,6 +59,14 @@ function deferredActor(record) {
59
59
  id: "factory-rule-dispatcher"
60
60
  };
61
61
  }
62
+ function externalActor(actor) {
63
+ return actor !== null && actor.type !== "human" && actor.type !== "agent" && actor.type !== "system";
64
+ }
65
+ /** A run start asks for consent; an external event asks before pulling a card back into a working lane. */
66
+ function requestsConsent(record, decision) {
67
+ if (decision.type === "invokeSkill") return true;
68
+ return decision.type === "transition" && isWorkingFactoryRuleStage(decision.stage) && externalActor(record.actor);
69
+ }
62
70
  function leaseIdentity(record, ownerId) {
63
71
  return {
64
72
  id: record.id,
@@ -274,17 +282,18 @@ var FactoryDecisionDispatcher = class {
274
282
  console.error("Factory proposal supersede failed", sanitizeDispatchError(error));
275
283
  }
276
284
  }
277
- /** Starting an agent run spends the project's compute and executes its code — the one effect a human owns. */
278
285
  async #needsApproval(record, decision) {
279
- if (decision.type !== "invokeSkill" || record.approvedAt !== null) return false;
280
- if (await this.#isAutoRunEnabled({
281
- orgId: record.orgId,
282
- factoryProjectId: record.factoryProjectId
283
- })) return false;
284
- return (record.workItemId ? await this.#storage.get({
286
+ if (record.approvedAt !== null || !requestsConsent(record, decision)) return false;
287
+ const item = record.workItemId ? await this.#storage.get({
285
288
  orgId: record.orgId,
286
289
  id: record.workItemId
287
- }) : null)?.autonomyArmedAt == null;
290
+ }) : null;
291
+ if (item && externallyAuthoredWorkItem(item)) return true;
292
+ if (item?.autonomyArmedAt != null) return false;
293
+ return !await this.#isAutoRunEnabled({
294
+ orgId: record.orgId,
295
+ factoryProjectId: record.factoryProjectId
296
+ });
288
297
  }
289
298
  async #executeDecision(record, decision) {
290
299
  const nextChain = [...record.causalChain, {
@@ -446,7 +455,8 @@ var FactoryDecisionDispatcher = class {
446
455
  return;
447
456
  }
448
457
  case "sendMessage": {
449
- const binding = decision.prepareBinding ? await this.#requireOrPrepareBinding(record, decision.role) : await this.#requireBinding(record, decision.role);
458
+ const binding = await this.#messageBinding(record, decision);
459
+ if (!binding) return;
450
460
  const startedBy = (record.workItemId ? await this.#storage.get({
451
461
  orgId: record.orgId,
452
462
  id: record.workItemId
@@ -601,6 +611,10 @@ var FactoryDecisionDispatcher = class {
601
611
  if (!binding) throw new FactoryDispatchError("session_unavailable", role ? `No active Factory binding for role ${role}.` : "No active Factory binding.");
602
612
  return binding;
603
613
  }
614
+ async #messageBinding(record, decision) {
615
+ if (decision.prepareBinding && decision.role !== void 0) return this.#requireOrPrepareBinding(record, decision.role);
616
+ return this.#findBinding(record, decision.role);
617
+ }
604
618
  async #requireOrPrepareBinding(record, role) {
605
619
  const binding = await this.#findBinding(record, role);
606
620
  if (binding) {
@@ -1 +1 @@
1
- {"version":3,"file":"dispatcher.js","names":["#controller","#transitionService","#storage","#ownerId","#isAutoRunEnabled","#reconcileToolResults","#prepareBinding","#primeCredentials","#feedReader","#resolveLinkedWorkItemParentId","#maxInFlight","#staleBindingSweepIntervalMs","#staleBindingTtlMs","#reconcileIntervalMs","#skillCompletionObservationTimeoutMs","#inFlight","#timer","#tick","#activeClaim","#claimAndStart","#maybeSweepStaleBindings","#maybeReconcileToolResults","#track","#dispatchPendingStart","#dispatchDecision","#reconcileInFlight","#lastReconcileAt","#lastStaleBindingSweepAt","#needsApproval","#supersedeProposals","#withLease","#executeDecision","#requireItem","#findBinding","#findSession","#upsertLinkedItem","#requireOrPrepareBinding","#switchThread","#requireBinding","#requireSession"],"sources":["../../src/rules/dispatcher.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\n\nimport type { MastraCodeState } from '@mastra/code-sdk/schema';\nimport type { AgentController, AgentControllerEventListener, Session } from '@mastra/core/agent-controller';\nimport { RequestContext } from '@mastra/core/request-context';\n\nimport { resolvePromptInvocation, resolveSkillInvocation } from '../skills/service.js';\nimport type { SkillSession } from '../skills/service.js';\nimport { withWorkItemFeed } from '../storage/domains/comments/feed-context.js';\nimport type { FactoryFeedReader } from '../storage/domains/comments/feed-context.js';\nimport type {\n FactoryDeferredDecisionRecord,\n FactoryPendingStartRecord,\n FactoryRunBindingRecord,\n WorkItemRow,\n WorkItemsStorage,\n} from '../storage/domains/work-items/base.js';\nimport { FACTORY_RULE_MATERIALIZATION_KEY } from '../storage/domains/work-items/base.js';\nimport { FactoryDispatchError, factoryDispatchFailureCode } from './dispatch-errors.js';\nimport type { FactoryTransitionService } from './transition-service.js';\nimport type { FactoryCommitDecision, FactoryRuleActor, FactoryRuleCausalEntry } from './types.js';\nimport { FACTORY_RULE_STAGES } from './types.js';\nimport { MAX_FACTORY_RULE_CAUSAL_DEPTH, validateFactoryRuleDecision } from './validation.js';\n\nconst LEASE_MS = 30_000;\nconst POLL_MS = 1_000;\nconst BATCH_SIZE = 10;\nconst MAX_ATTEMPTS = 5;\nconst MAX_ERROR_LENGTH = 512;\nconst MAX_BACKOFF_MS = 60_000;\nconst SKILL_COMPLETION_OBSERVATION_TIMEOUT_MS = 10 * 60_000;\n// Dispatches can legitimately run for minutes. Woken skill invocations hold\n// capacity until their agent run reaches a terminal state; binding preparation\n// also runs detached from the poll loop under this concurrency cap.\nconst MAX_IN_FLIGHT = 25;\n// Staleness sweep: legacy/leaked active bindings (item deleted, transition\n// path bypassed, or pre-dating terminal-stage revocation) are revoked on a\n// slow cadence so the per-tick reconcile walk stays bounded.\nconst STALE_BINDING_SWEEP_INTERVAL_MS = 10 * 60_000;\nconst STALE_BINDING_TTL_MS = 24 * 60 * 60_000;\n// The bound-thread reconcile walk reads a cursor + messages per binding; it\n// exists to catch results missed at run end, so it runs on a slow cadence off\n// the claim path rather than on every 1s tick.\nconst RECONCILE_INTERVAL_MS = 30_000;\n\nfunction waitForAgentEndOrTimeout(agentEnd: Promise<void>, timeoutMs: number): Promise<boolean> {\n return new Promise(resolve => {\n const timeout = setTimeout(() => resolve(false), timeoutMs);\n timeout.unref?.();\n void agentEnd.then(() => {\n clearTimeout(timeout);\n resolve(true);\n });\n });\n}\n\ninterface ThreadSwitchSession {\n thread: {\n switch(input: { threadId: string }): Promise<unknown>;\n };\n}\n\ninterface FactoryNotificationResult {\n persisted?: Promise<unknown>;\n accepted?: Promise<{\n action?: string;\n output?: { consumeStream(): Promise<unknown> };\n }>;\n}\n\ninterface DispatcherSession extends SkillSession {\n thread: {\n switch(input: { threadId: string }): Promise<unknown>;\n listActiveMessages(): Promise<Array<{ id: string }>>;\n };\n abort(): void;\n sendSignal(\n input: { id: string; type: 'user'; tagName: 'user'; contents: string },\n options: { requestContext: RequestContext; requireDelivery?: boolean },\n ): { accepted: Promise<{ accepted: true; runId?: string; action?: string }> };\n subscribe(listener: AgentControllerEventListener): () => void;\n}\n\ntype FactoryController = Pick<AgentController<MastraCodeState>, 'getSessionByResource'>;\ntype BoundDispatcherSession = Session<MastraCodeState>;\n\nexport interface FactoryBindingPreparationInput {\n record: FactoryDeferredDecisionRecord;\n item: WorkItemRow;\n role: string;\n}\n\nexport interface FactoryDecisionDispatcherOptions {\n controller: FactoryController;\n transitionService: Pick<FactoryTransitionService, 'transition'>;\n storage: WorkItemsStorage;\n ownerId?: string;\n /** `false` parks `invokeSkill` effects as `proposed`; every other effect still runs. */\n isAutoRunEnabled: (tenant: { orgId: string; factoryProjectId: string }) => Promise<boolean>;\n reconcileToolResults?: () => Promise<void>;\n prepareBinding?: (input: FactoryBindingPreparationInput) => Promise<void>;\n primeCredentials?: (tenant: { orgId: string; userId: string }) => Promise<void>;\n /** Injects the work item's recent comments into skill-invocation kickoffs. */\n feedReader?: FactoryFeedReader;\n resolveLinkedWorkItemParentId?: (input: {\n orgId: string;\n decision: Extract<FactoryCommitDecision, { type: 'upsertLinkedWorkItem' }>;\n }) => Promise<string | null>;\n maxInFlight?: number;\n /** How often the stale-binding sweep runs. Defaults to 10 minutes. */\n staleBindingSweepIntervalMs?: number;\n /** Active bindings older than this are revoked by the sweep. Defaults to 24 hours. */\n staleBindingTtlMs?: number;\n /** How often the bound-thread reconcile walk runs. Defaults to 30 seconds. */\n reconcileIntervalMs?: number;\n /** How long to wait for a run's terminal event before failing for retry. Defaults to 10 minutes. */\n skillCompletionObservationTimeoutMs?: number;\n}\n\nfunction positiveMs(value: number | undefined, fallback: number): number {\n return typeof value === 'number' && Number.isFinite(value) && value > 0 ? Math.floor(value) : fallback;\n}\n\nfunction sanitizeDispatchError(error: unknown): string {\n const message = error instanceof Error ? error.message : String(error);\n return message\n .replace(/\\b(?:bearer|token|api[-_ ]?key|authorization)\\s*[:=]?\\s*[^\\s,;]+/gi, '[redacted]')\n .slice(0, MAX_ERROR_LENGTH);\n}\n\nfunction retryAt(now: Date, attempts: number): Date {\n return new Date(now.getTime() + Math.min(1_000 * 2 ** Math.max(0, attempts - 1), MAX_BACKOFF_MS));\n}\n\nfunction externalSourceForDecision(decision: Extract<FactoryCommitDecision, { type: 'upsertLinkedWorkItem' }>) {\n const [integrationId, type] =\n decision.source === 'github-pr'\n ? ['github', 'pull-request']\n : decision.source === 'github-issue'\n ? ['github', 'issue']\n : decision.source === 'linear-issue'\n ? ['linear', 'issue']\n : ['factory', 'manual'];\n return { integrationId, type, externalId: decision.sourceKey, url: decision.url ?? undefined };\n}\n\nfunction deferredActor(record: FactoryDeferredDecisionRecord): FactoryRuleActor {\n const actor = record.actor;\n if (\n actor?.type === 'github' &&\n typeof actor.login === 'string' &&\n typeof actor.trusted === 'boolean' &&\n typeof actor.factoryAuthored === 'boolean'\n ) {\n return {\n type: 'github',\n login: actor.login,\n trusted: actor.trusted,\n factoryAuthored: actor.factoryAuthored,\n };\n }\n return { type: 'system', id: 'factory-rule-dispatcher' };\n}\n\nfunction leaseIdentity(\n record: Pick<FactoryDeferredDecisionRecord | FactoryPendingStartRecord, 'id' | 'orgId' | 'factoryProjectId'>,\n ownerId: string,\n) {\n return { id: record.id, orgId: record.orgId, factoryProjectId: record.factoryProjectId, ownerId };\n}\n\nasync function awaitNotification(\n send: () => Promise<FactoryNotificationResult>,\n requireDelivery = false,\n): Promise<{ action?: string } | undefined> {\n try {\n const notification = await send();\n const [, accepted] = await Promise.all([notification.persisted, notification.accepted]);\n if (!accepted) {\n if (requireDelivery) {\n throw new FactoryDispatchError(\n 'notification_delivery_failed',\n 'Factory notification was persisted without agent delivery.',\n );\n }\n return undefined;\n }\n if (!requireDelivery) return accepted;\n if (accepted.action === 'wake') {\n if (!accepted.output) {\n throw new FactoryDispatchError('notification_delivery_failed', 'Factory notification wake had no output.');\n }\n await accepted.output.consumeStream();\n return accepted;\n }\n if (accepted.action !== 'deliver') {\n throw new FactoryDispatchError(\n 'notification_delivery_failed',\n `Factory notification did not reach the agent (${String(accepted.action)}).`,\n );\n }\n return accepted;\n } catch (error) {\n if (error instanceof FactoryDispatchError) throw error;\n throw new FactoryDispatchError(\n 'notification_delivery_failed',\n `Factory notification delivery failed: ${sanitizeDispatchError(error)}`,\n { cause: error },\n );\n }\n}\n\nexport class FactoryDecisionDispatcher {\n readonly #controller: FactoryController;\n readonly #transitionService: Pick<FactoryTransitionService, 'transition'>;\n readonly #storage: WorkItemsStorage;\n readonly #ownerId: string;\n readonly #isAutoRunEnabled: (tenant: { orgId: string; factoryProjectId: string }) => Promise<boolean>;\n readonly #reconcileToolResults?: () => Promise<void>;\n readonly #prepareBinding?: (input: FactoryBindingPreparationInput) => Promise<void>;\n readonly #primeCredentials?: (tenant: { orgId: string; userId: string }) => Promise<void>;\n readonly #feedReader?: FactoryFeedReader;\n readonly #resolveLinkedWorkItemParentId?: FactoryDecisionDispatcherOptions['resolveLinkedWorkItemParentId'];\n readonly #maxInFlight: number;\n readonly #staleBindingSweepIntervalMs: number;\n readonly #staleBindingTtlMs: number;\n #lastStaleBindingSweepAt?: Date;\n readonly #reconcileIntervalMs: number;\n readonly #skillCompletionObservationTimeoutMs: number;\n #lastReconcileAt?: Date;\n #reconcileInFlight?: Promise<void>;\n #timer?: ReturnType<typeof setInterval>;\n #activeClaim?: Promise<void>;\n readonly #inFlight = new Set<Promise<void>>();\n\n constructor(options: FactoryDecisionDispatcherOptions) {\n this.#controller = options.controller;\n this.#transitionService = options.transitionService;\n this.#storage = options.storage;\n this.#ownerId = options.ownerId ?? `factory-dispatcher:${randomUUID()}`;\n this.#isAutoRunEnabled = options.isAutoRunEnabled;\n this.#reconcileToolResults = options.reconcileToolResults;\n this.#prepareBinding = options.prepareBinding;\n this.#primeCredentials = options.primeCredentials;\n this.#feedReader = options.feedReader;\n this.#resolveLinkedWorkItemParentId = options.resolveLinkedWorkItemParentId;\n const maxInFlight = options.maxInFlight ?? MAX_IN_FLIGHT;\n this.#maxInFlight = Number.isFinite(maxInFlight) && maxInFlight > 0 ? Math.floor(maxInFlight) : MAX_IN_FLIGHT;\n this.#staleBindingSweepIntervalMs = positiveMs(\n options.staleBindingSweepIntervalMs,\n STALE_BINDING_SWEEP_INTERVAL_MS,\n );\n this.#staleBindingTtlMs = positiveMs(options.staleBindingTtlMs, STALE_BINDING_TTL_MS);\n this.#reconcileIntervalMs = positiveMs(options.reconcileIntervalMs, RECONCILE_INTERVAL_MS);\n this.#skillCompletionObservationTimeoutMs = positiveMs(\n options.skillCompletionObservationTimeoutMs,\n SKILL_COMPLETION_OBSERVATION_TIMEOUT_MS,\n );\n }\n\n start(): void {\n if (this.#timer) return;\n void this.#tick();\n this.#timer = setInterval(() => void this.#tick(), POLL_MS);\n this.#timer.unref?.();\n }\n\n async stop(): Promise<void> {\n if (this.#timer) clearInterval(this.#timer);\n this.#timer = undefined;\n await this.#activeClaim;\n await Promise.allSettled([...this.#inFlight]);\n }\n\n async runOnce(now = new Date()): Promise<void> {\n await Promise.all(await this.#claimAndStart(now));\n }\n\n /**\n * Claims a batch and starts dispatches without awaiting their completion.\n * Dispatches can legitimately take minutes (skill kickoffs consume the\n * agent's run stream; binding preparation provisions sandboxes), so awaiting\n * them here would freeze the poll loop and starve every other queued\n * decision. In-flight records stay protected from re-claim by lease renewal.\n */\n async #claimAndStart(now: Date): Promise<Array<Promise<void>>> {\n // Fire-and-forget like the reconcile walk: the sweep reads every active\n // binding, so awaiting it would stretch the tick as the active set grows.\n void this.#maybeSweepStaleBindings(now);\n this.#maybeReconcileToolResults(now);\n const capacity = this.#maxInFlight - this.#inFlight.size;\n if (capacity <= 0) return [];\n const limit = Math.min(BATCH_SIZE, capacity);\n const leaseExpiresAt = new Date(now.getTime() + LEASE_MS);\n // Starts are claimed before deferred decisions: a pending start is a user\n // waiting on a brand-new session, while a deferred decision is a background\n // continuation of one that is already running. A deep decision queue must\n // never starve new sessions out of the tick.\n const starts = await this.#storage.claimPendingStarts({\n ownerId: this.#ownerId,\n now,\n leaseExpiresAt,\n limit,\n });\n const decisionsLimit = limit - starts.length;\n const decisions =\n decisionsLimit > 0\n ? await this.#storage.claimDeferredDecisions({\n ownerId: this.#ownerId,\n now,\n leaseExpiresAt,\n limit: decisionsLimit,\n })\n : [];\n return [\n ...starts.map(start => this.#track(this.#dispatchPendingStart(start, now))),\n ...decisions.map(decision => this.#track(this.#dispatchDecision(decision, now))),\n ];\n }\n\n /**\n * Throttled, coalesced, non-blocking bound-thread reconcile: dispatch\n * claiming never waits behind cursor + message reads, and overlapping runs\n * are skipped while one is still in flight.\n */\n #maybeReconcileToolResults(now: Date): void {\n if (!this.#reconcileToolResults || this.#reconcileInFlight) return;\n if (this.#lastReconcileAt && now.getTime() - this.#lastReconcileAt.getTime() < this.#reconcileIntervalMs) return;\n this.#lastReconcileAt = now;\n const run = this.#reconcileToolResults()\n .catch(error => {\n console.error('Factory tool-result reconcile failed', sanitizeDispatchError(error));\n })\n .finally(() => {\n this.#reconcileInFlight = undefined;\n });\n this.#reconcileInFlight = run;\n this.#track(run);\n }\n\n /** Slow-cadence revocation of leaked/legacy bindings; failures never block the claim path. */\n async #maybeSweepStaleBindings(now: Date): Promise<void> {\n // The first tick only anchors the cadence: sweeping at boot would race the\n // startup reconcile that is still draining trailing tool results.\n if (!this.#lastStaleBindingSweepAt) {\n this.#lastStaleBindingSweepAt = now;\n return;\n }\n if (now.getTime() - this.#lastStaleBindingSweepAt.getTime() < this.#staleBindingSweepIntervalMs) return;\n this.#lastStaleBindingSweepAt = now;\n try {\n const revoked = await this.#storage.revokeStaleRunBindings({\n olderThan: new Date(now.getTime() - this.#staleBindingTtlMs),\n now,\n });\n if (revoked > 0) console.info(`Factory stale-binding sweep revoked ${revoked} binding(s)`);\n } catch (error) {\n console.error('Factory stale-binding sweep failed', sanitizeDispatchError(error));\n }\n }\n\n #track(dispatch: Promise<void>): Promise<void> {\n this.#inFlight.add(dispatch);\n void dispatch.catch(() => {}).then(() => this.#inFlight.delete(dispatch));\n return dispatch;\n }\n\n async #tick(): Promise<void> {\n if (this.#activeClaim) return;\n this.#activeClaim = this.#claimAndStart(new Date()).then(\n dispatches => {\n for (const dispatch of dispatches) {\n dispatch.catch(error => {\n console.error('Factory decision dispatch failed', sanitizeDispatchError(error));\n });\n }\n },\n error => {\n console.error('Factory decision dispatch cycle failed', sanitizeDispatchError(error));\n },\n );\n try {\n await this.#activeClaim;\n } finally {\n this.#activeClaim = undefined;\n }\n }\n\n async #dispatchDecision(record: FactoryDeferredDecisionRecord, now: Date): Promise<void> {\n let executionCompleted = false;\n try {\n const decision = validateFactoryRuleDecision(record.decision, record.causalChain.length);\n if (decision.type === 'reject') throw new Error('Deferred Factory decisions cannot reject.');\n if (await this.#needsApproval(record, decision)) {\n const proposed = await this.#storage.proposeDeferredDecision(leaseIdentity(record, this.#ownerId), new Date());\n if (!proposed) throw new Error('Factory decision lease was lost before approval could be requested.');\n return;\n }\n await this.#supersedeProposals(record, decision);\n await this.#withLease(\n async leaseExpiresAt =>\n this.#storage.renewDeferredDecisionLease(leaseIdentity(record, this.#ownerId), leaseExpiresAt),\n async () => this.#executeDecision(record, decision),\n );\n executionCompleted = true;\n const completed = await this.#storage.completeDeferredDecision(leaseIdentity(record, this.#ownerId), new Date());\n if (!completed) throw new Error('Factory decision lease was lost before completion.');\n } catch (error) {\n const terminal = record.attempts >= MAX_ATTEMPTS;\n await this.#storage.failDeferredDecision({\n ...leaseIdentity(record, this.#ownerId),\n now: new Date(),\n availableAt: retryAt(now, record.attempts),\n lastError: sanitizeDispatchError(error),\n failureCode: factoryDispatchFailureCode(error),\n terminal,\n advanceDeliveryGeneration: !executionCompleted,\n });\n }\n }\n\n /**\n * A proposal is a question: \"should this run start?\" Once that run is\n * starting anyway — because a person approved a later copy, or armed the item\n * — the question has been answered and the card must stop asking it. Left\n * alone the badge outlives the work it describes, and the one affordance that\n * means \"the loop is stopped, answer this\" cries wolf.\n */\n async #supersedeProposals(record: FactoryDeferredDecisionRecord, decision: FactoryCommitDecision): Promise<void> {\n if (decision.type !== 'invokeSkill' || !record.workItemId) return;\n try {\n await this.#storage.supersedeDecisionsForWorkItem({\n orgId: record.orgId,\n factoryProjectId: record.factoryProjectId,\n workItemId: record.workItemId,\n role: decision.role,\n supersededAt: new Date(),\n });\n } catch (error) {\n // Best-effort: a stale badge is not worth failing the run it describes.\n console.error('Factory proposal supersede failed', sanitizeDispatchError(error));\n }\n }\n\n /** Starting an agent run spends the project's compute and executes its code — the one effect a human owns. */\n async #needsApproval(record: FactoryDeferredDecisionRecord, decision: FactoryCommitDecision): Promise<boolean> {\n if (decision.type !== 'invokeSkill' || record.approvedAt !== null) return false;\n if (await this.#isAutoRunEnabled({ orgId: record.orgId, factoryProjectId: record.factoryProjectId })) return false;\n // Withholding auto-run decides what the Factory may pick up on its own, not\n // whether it may finish work a person already handed it. Once someone starts\n // an item, the runs that carry it to review are that same request continuing.\n const item = record.workItemId ? await this.#storage.get({ orgId: record.orgId, id: record.workItemId }) : null;\n return item?.autonomyArmedAt == null;\n }\n\n async #executeDecision(record: FactoryDeferredDecisionRecord, decision: FactoryCommitDecision): Promise<void> {\n const nextChain: FactoryRuleCausalEntry[] = [\n ...(record.causalChain as FactoryRuleCausalEntry[]),\n { ingressId: record.idempotencyKey, decisionType: decision.type },\n ];\n if (nextChain.length > MAX_FACTORY_RULE_CAUSAL_DEPTH) throw new Error('Factory rule causal depth exceeded.');\n\n switch (decision.type) {\n case 'transition': {\n const item = await this.#requireItem(record);\n const result = await this.#transitionService.transition({\n orgId: record.orgId,\n factoryProjectId: record.factoryProjectId,\n workItemId: item.id,\n board: decision.board,\n stage: decision.stage,\n expectedRevision: item.revision,\n actor: { type: 'system', id: 'factory-rule-dispatcher' },\n ingress: { type: 'rule', identity: `decision:${record.idempotencyKey}` },\n cause: 'rule_decision',\n causalChain: nextChain,\n ...(decision.reenter ? { reenter: true } : {}),\n });\n if (result.status === 'rejected') throw new Error(`${result.code}: ${result.reason}`);\n const transitionMessage = decision.message;\n if (!transitionMessage) return;\n // Best-effort recipient lookup: no active binding (or no authenticated\n // session owner) means nobody is engaged with this item, so the\n // transition itself is the whole effect. A retry after a delivery\n // failure is safe because the transition replays by ingress identity.\n const binding = await this.#findBinding(record, transitionMessage.role);\n if (!binding) return;\n const startedBy = item.sessions[binding.role]?.startedBy;\n if (!startedBy) return;\n await this.#primeCredentials?.({ orgId: record.orgId, userId: startedBy });\n const requestContext = new RequestContext();\n requestContext.set('user', { workosId: startedBy, organizationId: record.orgId });\n const session = await this.#findSession(binding);\n if (!session) return;\n await awaitNotification(\n () =>\n session.sendNotificationSignal(\n {\n source: 'factory',\n kind: 'rule-message',\n summary: transitionMessage.text,\n priority: 'high',\n payload: { message: transitionMessage.text },\n sourceId: record.id,\n dedupeKey: record.idempotencyKey,\n },\n {\n ifActive: { behavior: 'deliver' },\n ifIdle: { behavior: 'wake' },\n requestContext,\n },\n ),\n true,\n );\n return;\n }\n case 'upsertLinkedWorkItem': {\n await this.#upsertLinkedItem(record, decision, nextChain);\n return;\n }\n case 'invokeSkill': {\n const binding = await this.#requireOrPrepareBinding(record, decision.role);\n const item = record.workItemId ? await this.#storage.get({ orgId: record.orgId, id: record.workItemId }) : null;\n const startedBy = item?.sessions[binding.role]?.startedBy;\n if (!startedBy) throw new Error(`Factory binding ${binding.id} has no authenticated session owner.`);\n await this.#primeCredentials?.({ orgId: record.orgId, userId: startedBy });\n const requestContext = new RequestContext();\n requestContext.set('user', { workosId: startedBy, organizationId: record.orgId });\n const resolved =\n decision.skillName === undefined\n ? await resolvePromptInvocation(this.#controller, {\n resourceId: binding.resourceId,\n prompt: decision.prompt,\n })\n : await resolveSkillInvocation(this.#controller, {\n resourceId: binding.resourceId,\n name: decision.skillName,\n arguments: decision.arguments,\n });\n const session = resolved.session as DispatcherSession;\n await this.#switchThread(session, binding);\n const deliveryId =\n record.deliveryGeneration === 0 ? record.id : `${record.id}:retry:${record.deliveryGeneration}`;\n const delivered = await session.thread.listActiveMessages();\n if (delivered.some(message => message.id === deliveryId)) return;\n // Safe under the replay guard above: it matches deliveryId, never prompt content.\n const kickoffContents = await withWorkItemFeed(\n this.#feedReader,\n { orgId: record.orgId, factoryProjectId: record.factoryProjectId, workItemId: record.workItemId },\n resolved.message,\n );\n if (decision.cancelInFlight) session.abort();\n const precedingMessage = decision.precedingMessage;\n if (precedingMessage) {\n await awaitNotification(() =>\n session.sendNotificationSignal(\n {\n source: 'factory',\n kind: 'stage-transition',\n summary: precedingMessage,\n priority: 'medium',\n payload: { message: precedingMessage },\n sourceId: `${record.id}:stage-transition`,\n dedupeKey: `${record.idempotencyKey}:stage-transition`,\n },\n {\n ifActive: { behavior: 'deliver' },\n ifIdle: { behavior: 'persist' },\n requestContext,\n },\n ),\n );\n }\n let resolveAgentEnd!: () => void;\n let agentEnd!: Promise<void>;\n // The run's own verdict, not the delivery's. A signal can reach the\n // agent perfectly and the run still die on a provider error or be\n // cancelled mid-flight; without this the decision reports success and\n // the break is invisible on the card.\n let endReason: 'complete' | 'aborted' | 'error' | 'suspended' | undefined;\n // Re-armed before a redelivery so the second send waits on its own run's\n // ending rather than seeing the one that already resolved.\n const armAgentEnd = () => {\n endReason = undefined;\n agentEnd = new Promise<void>(resolve => {\n resolveAgentEnd = resolve;\n });\n };\n armAgentEnd();\n const unsubscribe = session.subscribe(event => {\n if (event.type === 'agent_end') {\n endReason = event.reason;\n resolveAgentEnd();\n }\n });\n\n const sendKickoff = async () => {\n const result = session.sendSignal(\n {\n id: deliveryId,\n type: 'user',\n tagName: 'user',\n contents: kickoffContents,\n },\n // Without `requireDelivery` the session resolves `accepted` on the\n // next tick and swallows wake failures, so a kickoff that never\n // reached the agent would be marked succeeded and the thread would\n // stay empty forever.\n { requestContext, requireDelivery: true },\n );\n const settled = await result.accepted;\n if (settled.action !== 'wake' && settled.action !== 'deliver') {\n // An undefined action means the session did not verify delivery at\n // all — with `requireDelivery` set that is a contract violation, not\n // a success.\n throw new Error(`Factory skill invocation signal did not reach the agent (${String(settled.action)}).`);\n }\n return settled;\n };\n\n try {\n let settled = await sendKickoff();\n if (settled.action === 'deliver') {\n // `deliver` means the signal was queued onto a run that was already\n // in flight. If that run ends before draining its queue the prompt\n // is dropped silently: no turn starts, no error surfaces, and the\n // decision reports success while the card sits in its new stage with\n // nobody working. Signals persist under their generation-scoped id\n // (the same identity the replay guard above reads), so confirm the\n // message actually landed in the thread rather than trusting the ack.\n const landed = await session.thread.listActiveMessages();\n if (!landed.some(message => message.id === deliveryId)) {\n // The condition that resolves this is the in-flight run ending, so\n // wait for exactly that and redeliver into the idle session. A\n // backoff cannot work here: retries are sized in seconds and a turn\n // takes minutes, so every attempt lands on the same busy run and\n // the card burns its whole budget without the session ever having\n // had a chance to be free.\n if (!(await waitForAgentEndOrTimeout(agentEnd, this.#skillCompletionObservationTimeoutMs))) {\n throw new Error('Factory skill invocation is waiting on a run that has not ended.');\n }\n armAgentEnd();\n settled = await sendKickoff();\n if (settled.action !== 'wake') {\n throw new Error('Factory skill invocation was queued onto an ending run and never reached the agent.');\n }\n }\n }\n // A landed `deliver` still runs on the in-flight session, so the run's\n // terminal outcome matters as much as a fresh wake's: a run that ends\n // in error after accepting the prompt has still failed this decision.\n {\n const observed = await waitForAgentEndOrTimeout(agentEnd, this.#skillCompletionObservationTimeoutMs);\n if (!observed) {\n // A completed decision with no observed run end is exactly the\n // silent-stall failure mode: the card advances while nobody\n // works it. Fail non-terminally so the attempts/backoff\n // machinery redelivers — the delivery generation guarantees the\n // retry sends a fresh kickoff instead of hitting the replay\n // guard.\n throw new Error('Factory skill run terminal event was not observed before timeout.');\n } else if (endReason === 'error') {\n throw new Error('Factory skill run ended in error.');\n } else if (endReason === 'aborted') {\n // Retryable, though an abort reads as deliberate. The stream does\n // not say who aborted, and in practice the dominant cause is the\n // process going away underneath the run — an operator restarting\n // the server — not anyone deciding this work should stop. Treating\n // that as terminal dead-ends the card at attempt 1 with nothing on\n // the board to press. A spurious retry is bounded by MAX_ATTEMPTS;\n // a dead card costs a human a manual nudge.\n throw new Error('Factory skill run was aborted before it finished.');\n }\n }\n } finally {\n unsubscribe();\n }\n return;\n }\n case 'sendMessage': {\n const binding = decision.prepareBinding\n ? await this.#requireOrPrepareBinding(record, decision.role)\n : await this.#requireBinding(record, decision.role);\n const item = record.workItemId ? await this.#storage.get({ orgId: record.orgId, id: record.workItemId }) : null;\n const startedBy = item?.sessions[binding.role]?.startedBy;\n if (!startedBy) throw new Error(`Factory binding ${binding.id} has no authenticated session owner.`);\n await this.#primeCredentials?.({ orgId: record.orgId, userId: startedBy });\n const requestContext = new RequestContext();\n requestContext.set('user', { workosId: startedBy, organizationId: record.orgId });\n const session = await this.#requireSession(binding);\n await awaitNotification(\n () =>\n session.sendNotificationSignal(\n {\n source: 'factory',\n kind: 'rule-message',\n summary: decision.message,\n priority: decision.priority ?? 'high',\n payload: { message: decision.message },\n sourceId: record.id,\n dedupeKey: record.idempotencyKey,\n },\n {\n ifActive: { behavior: 'deliver' },\n ifIdle: { behavior: decision.idleBehavior ?? 'wake' },\n requestContext,\n },\n ),\n true,\n );\n return;\n }\n case 'notify': {\n const binding = await this.#requireBinding(record);\n const session = await this.#requireSession(binding);\n await awaitNotification(() =>\n session.sendNotificationSignal({\n source: 'factory',\n kind: 'rule-notification',\n summary: decision.title,\n payload: { body: decision.body, level: decision.level },\n sourceId: record.id,\n dedupeKey: record.idempotencyKey,\n }),\n );\n }\n }\n }\n\n async #upsertLinkedItem(\n record: FactoryDeferredDecisionRecord,\n decision: Extract<FactoryCommitDecision, { type: 'upsertLinkedWorkItem' }>,\n causalChain: FactoryRuleCausalEntry[],\n ): Promise<void> {\n const parentWorkItemId =\n record.workItemId ??\n (await this.#resolveLinkedWorkItemParentId?.({\n orgId: record.orgId,\n decision,\n })) ??\n null;\n let result = await this.#storage.upsert({\n orgId: record.orgId,\n userId: 'factory-rule-dispatcher',\n factoryProjectId: record.factoryProjectId,\n input: {\n externalSource: externalSourceForDecision(decision),\n parentWorkItemId,\n title: decision.title,\n stages: ['intake'],\n sessions: {},\n metadata: { ...decision.metadata, [FACTORY_RULE_MATERIALIZATION_KEY]: record.idempotencyKey },\n },\n reuseMode: 'preserve',\n });\n if (!result.item.parentWorkItemId && parentWorkItemId) {\n const item = await this.#storage.setParentWorkItemIfMissing({\n orgId: record.orgId,\n id: result.item.id,\n userId: 'factory-rule-dispatcher',\n parentWorkItemId,\n });\n if (item) result = { ...result, item };\n }\n const materializedByDecision = result.item.metadata?.[FACTORY_RULE_MATERIALIZATION_KEY] === record.idempotencyKey;\n if (!materializedByDecision && (decision.stage === 'intake' || !result.item.stages.includes('intake'))) return;\n\n const board = decision.board;\n let expectedRevision = result.item.revision;\n if (materializedByDecision) {\n const initial = await this.#transitionService.transition({\n orgId: record.orgId,\n factoryProjectId: record.factoryProjectId,\n workItemId: result.item.id,\n board,\n stage: 'intake',\n expectedRevision,\n actor: deferredActor(record),\n ingress: { type: 'rule', identity: `decision:${record.idempotencyKey}:${result.item.id}:initial-entry` },\n cause: 'linked_item_materialized',\n causalChain,\n initialEntry: true,\n });\n if (initial.status === 'rejected') {\n if (result.created) await this.#storage.delete({ orgId: record.orgId, id: result.item.id });\n throw new Error(`${initial.code}: ${initial.reason}`);\n }\n expectedRevision = initial.revision;\n }\n if (decision.stage === 'intake') return;\n\n const moved = await this.#transitionService.transition({\n orgId: record.orgId,\n factoryProjectId: record.factoryProjectId,\n workItemId: result.item.id,\n board,\n stage: decision.stage,\n expectedRevision,\n actor: { type: 'system', id: 'factory-rule-dispatcher' },\n ingress: { type: 'rule', identity: `decision:${record.idempotencyKey}:${result.item.id}:destination` },\n cause: materializedByDecision ? 'linked_item_materialized' : 'linked_item_reconciled',\n causalChain,\n });\n if (moved.status === 'rejected') throw new Error(`${moved.code}: ${moved.reason}`);\n }\n\n async #requireItem(record: FactoryDeferredDecisionRecord) {\n if (!record.workItemId) throw new Error('Factory decision is not linked to a work item.');\n const item = await this.#storage.get({ orgId: record.orgId, id: record.workItemId });\n if (!item) throw new Error('Factory work item not found.');\n return item;\n }\n\n async #findBinding(\n record: FactoryDeferredDecisionRecord,\n role?: string,\n ): Promise<FactoryRunBindingRecord | undefined> {\n if (!record.workItemId) throw new Error('Factory decision is not linked to a work item.');\n const bindings = await this.#storage.listRunBindings(record.orgId, record.factoryProjectId, record.workItemId);\n return bindings\n .filter(candidate => candidate.status === 'active' && (role === undefined || candidate.role === role))\n .sort((left, right) => {\n if (role === undefined && left.role === 'work' && right.role !== 'work') return -1;\n if (role === undefined && right.role === 'work' && left.role !== 'work') return 1;\n return right.createdAt.getTime() - left.createdAt.getTime() || left.id.localeCompare(right.id);\n })[0];\n }\n\n async #requireBinding(record: FactoryDeferredDecisionRecord, role?: string): Promise<FactoryRunBindingRecord> {\n const binding = await this.#findBinding(record, role);\n if (!binding) {\n throw new FactoryDispatchError(\n 'session_unavailable',\n role ? `No active Factory binding for role ${role}.` : 'No active Factory binding.',\n );\n }\n return binding;\n }\n\n async #requireOrPrepareBinding(\n record: FactoryDeferredDecisionRecord,\n role: string,\n ): Promise<FactoryRunBindingRecord> {\n const binding = await this.#findBinding(record, role);\n if (binding) {\n const session = await this.#controller.getSessionByResource(binding.resourceId);\n if (session) return binding;\n }\n if (!this.#prepareBinding) {\n throw new FactoryDispatchError(\n 'session_unavailable',\n binding ? 'Bound Factory session not found.' : `No active Factory binding for role ${role}.`,\n );\n }\n const item = await this.#requireItem(record);\n await this.#prepareBinding({ record, item, role });\n return this.#requireBinding(record, role);\n }\n\n async #findSession(binding: FactoryRunBindingRecord): Promise<BoundDispatcherSession | undefined> {\n const session = await this.#controller.getSessionByResource(binding.resourceId);\n if (!session) return undefined;\n await this.#switchThread(session, binding);\n return session;\n }\n\n async #requireSession(binding: FactoryRunBindingRecord): Promise<BoundDispatcherSession> {\n const session = await this.#findSession(binding);\n if (!session) throw new FactoryDispatchError('session_unavailable', 'Bound Factory session not found.');\n return session;\n }\n\n async #switchThread(session: ThreadSwitchSession, binding: FactoryRunBindingRecord): Promise<void> {\n await session.thread.switch({ threadId: binding.threadId });\n }\n\n async #withLease(\n renew: (leaseExpiresAt: Date) => Promise<unknown | null>,\n effect: () => Promise<void>,\n ): Promise<void> {\n let renewalFailure: unknown;\n let renewal = Promise.resolve();\n const timer = setInterval(\n () => {\n renewal = renewal.then(async () => {\n try {\n const renewed = await renew(new Date(Date.now() + LEASE_MS));\n if (!renewed) renewalFailure = new Error('Factory dispatch lease was lost during execution.');\n } catch (error) {\n renewalFailure = error;\n }\n });\n },\n Math.floor(LEASE_MS / 3),\n );\n timer.unref?.();\n try {\n await effect();\n await renewal;\n if (renewalFailure) throw renewalFailure;\n } finally {\n clearInterval(timer);\n await renewal;\n }\n }\n\n async #dispatchPendingStart(record: FactoryPendingStartRecord, now: Date): Promise<void> {\n try {\n await this.#withLease(\n async leaseExpiresAt =>\n this.#storage.renewPendingStartLease(leaseIdentity(record, this.#ownerId), leaseExpiresAt),\n async () => {\n if (record.message === null) return;\n const bindings = await this.#storage.listRunBindings(record.orgId, record.factoryProjectId);\n const binding = bindings.find(\n candidate => candidate.id === record.bindingId && candidate.status === 'active',\n );\n if (!binding) {\n throw new FactoryDispatchError(\n 'session_unavailable',\n 'Prepared Factory binding is unavailable or revoked.',\n );\n }\n // Wake runs build the Factory workspace, which requires the\n // authenticated session owner on the request context.\n const item = await this.#storage.get({ orgId: record.orgId, id: binding.workItemId });\n const startedBy = item?.sessions[binding.role]?.startedBy;\n if (!startedBy) throw new Error(`Factory binding ${binding.id} has no authenticated session owner.`);\n await this.#primeCredentials?.({ orgId: record.orgId, userId: startedBy });\n const requestContext = new RequestContext();\n requestContext.set('user', { workosId: startedBy, organizationId: record.orgId });\n const session = await this.#requireSession(binding);\n let resolveAgentEnd!: () => void;\n let agentEnd!: Promise<void>;\n // The run's own verdict, not the delivery's: a kickoff delivered\n // into a run that is already terminating is consumed without\n // execution, and completing the pending start on the delivery ack\n // alone strands the card with a success ledger entry.\n let endReason: 'complete' | 'aborted' | 'error' | 'suspended' | undefined;\n const armAgentEnd = () => {\n endReason = undefined;\n agentEnd = new Promise<void>(resolve => {\n resolveAgentEnd = resolve;\n });\n };\n armAgentEnd();\n const unsubscribe = session.subscribe(event => {\n if (event.type === 'agent_end') {\n endReason = event.reason;\n resolveAgentEnd();\n }\n });\n const sendKickoff = (dedupeKey: string) =>\n awaitNotification(\n () =>\n session.sendNotificationSignal(\n {\n source: 'factory',\n kind: 'run-kickoff',\n summary: record.message!,\n priority: 'high',\n payload: { message: record.message },\n sourceId: record.id,\n dedupeKey,\n },\n { ifActive: { behavior: 'deliver' }, ifIdle: { behavior: 'wake' }, requestContext },\n ),\n true,\n );\n try {\n let settled = await sendKickoff(`factory-kickoff:${record.kickoffKey}`);\n if (settled?.action === 'deliver') {\n // `deliver` only proves the signal was queued onto a run already\n // in flight. If that run ends without draining its queue the\n // kickoff is dropped silently. There is no per-notification\n // \"processed\" signal, so wait for the in-flight run to end and\n // redeliver into the idle session unconditionally — the\n // generation-scoped dedupeKey defeats inbox dedupe and the\n // kickoff key keeps a duplicate run bounded, while a dropped\n // kickoff strands the card forever.\n if (!(await waitForAgentEndOrTimeout(agentEnd, this.#skillCompletionObservationTimeoutMs))) {\n throw new Error('Factory kickoff is waiting on a run that has not ended.');\n }\n armAgentEnd();\n settled = await sendKickoff(`factory-kickoff:${record.kickoffKey}:retry:${record.attempts}`);\n if (settled?.action !== 'wake') {\n throw new Error('Factory kickoff was queued onto an ending run and never reached the agent.');\n }\n }\n const observed = await waitForAgentEndOrTimeout(agentEnd, this.#skillCompletionObservationTimeoutMs);\n if (!observed) {\n throw new Error('Factory kickoff run terminal event was not observed before timeout.');\n } else if (endReason === 'error') {\n throw new Error('Factory kickoff run ended in error.');\n } else if (endReason === 'aborted') {\n // Retryable for the same reason as skill decisions: the dominant\n // cause is the process going away underneath the run, not a\n // deliberate stop, and a spurious retry is bounded by\n // MAX_ATTEMPTS while a dead card costs a human a manual nudge.\n throw new Error('Factory kickoff run was aborted before it finished.');\n }\n } finally {\n unsubscribe();\n }\n },\n );\n const completed = await this.#storage.completePendingStart(leaseIdentity(record, this.#ownerId), new Date());\n if (!completed) throw new Error('Factory kickoff lease was lost before completion.');\n } catch (error) {\n await this.#storage.failPendingStart({\n ...leaseIdentity(record, this.#ownerId),\n now: new Date(),\n availableAt: retryAt(now, record.attempts),\n lastError: sanitizeDispatchError(error),\n failureCode: factoryDispatchFailureCode(error),\n terminal: record.attempts >= MAX_ATTEMPTS,\n });\n }\n }\n}\n\nexport const FACTORY_DISPATCH_CONSTANTS = {\n leaseMs: LEASE_MS,\n pollMs: POLL_MS,\n batchSize: BATCH_SIZE,\n maxAttempts: MAX_ATTEMPTS,\n maxErrorLength: MAX_ERROR_LENGTH,\n maxBackoffMs: MAX_BACKOFF_MS,\n skillCompletionObservationTimeoutMs: SKILL_COMPLETION_OBSERVATION_TIMEOUT_MS,\n maxInFlight: MAX_IN_FLIGHT,\n stages: FACTORY_RULE_STAGES,\n} as const;\n"],"mappings":"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"}
1
+ {"version":3,"file":"dispatcher.js","names":["#controller","#transitionService","#storage","#ownerId","#isAutoRunEnabled","#reconcileToolResults","#prepareBinding","#primeCredentials","#feedReader","#resolveLinkedWorkItemParentId","#maxInFlight","#staleBindingSweepIntervalMs","#staleBindingTtlMs","#reconcileIntervalMs","#skillCompletionObservationTimeoutMs","#inFlight","#timer","#tick","#activeClaim","#claimAndStart","#maybeSweepStaleBindings","#maybeReconcileToolResults","#track","#dispatchPendingStart","#dispatchDecision","#reconcileInFlight","#lastReconcileAt","#lastStaleBindingSweepAt","#needsApproval","#supersedeProposals","#withLease","#executeDecision","#requireItem","#findBinding","#findSession","#upsertLinkedItem","#requireOrPrepareBinding","#switchThread","#messageBinding","#requireSession","#requireBinding"],"sources":["../../src/rules/dispatcher.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\n\nimport type { MastraCodeState } from '@mastra/code-sdk/schema';\nimport type { AgentController, AgentControllerEventListener, Session } from '@mastra/core/agent-controller';\nimport { RequestContext } from '@mastra/core/request-context';\n\nimport { resolvePromptInvocation, resolveSkillInvocation } from '../skills/service.js';\nimport type { SkillSession } from '../skills/service.js';\nimport { withWorkItemFeed } from '../storage/domains/comments/feed-context.js';\nimport type { FactoryFeedReader } from '../storage/domains/comments/feed-context.js';\nimport type {\n FactoryDeferredDecisionRecord,\n FactoryPendingStartRecord,\n FactoryRunBindingRecord,\n WorkItemRow,\n WorkItemsStorage,\n} from '../storage/domains/work-items/base.js';\nimport { FACTORY_RULE_MATERIALIZATION_KEY } from '../storage/domains/work-items/base.js';\nimport { FactoryDispatchError, factoryDispatchFailureCode } from './dispatch-errors.js';\nimport type { FactoryTransitionService } from './transition-service.js';\nimport type { FactoryCommitDecision, FactoryRuleActor, FactoryRuleCausalEntry } from './types.js';\nimport { externallyAuthoredWorkItem, FACTORY_RULE_STAGES, isWorkingFactoryRuleStage } from './types.js';\nimport { MAX_FACTORY_RULE_CAUSAL_DEPTH, validateFactoryRuleDecision } from './validation.js';\n\nconst LEASE_MS = 30_000;\nconst POLL_MS = 1_000;\nconst BATCH_SIZE = 10;\nconst MAX_ATTEMPTS = 5;\nconst MAX_ERROR_LENGTH = 512;\nconst MAX_BACKOFF_MS = 60_000;\nconst SKILL_COMPLETION_OBSERVATION_TIMEOUT_MS = 10 * 60_000;\n// Dispatches can legitimately run for minutes. Woken skill invocations hold\n// capacity until their agent run reaches a terminal state; binding preparation\n// also runs detached from the poll loop under this concurrency cap.\nconst MAX_IN_FLIGHT = 25;\n// Staleness sweep: legacy/leaked active bindings (item deleted, transition\n// path bypassed, or pre-dating terminal-stage revocation) are revoked on a\n// slow cadence so the per-tick reconcile walk stays bounded.\nconst STALE_BINDING_SWEEP_INTERVAL_MS = 10 * 60_000;\nconst STALE_BINDING_TTL_MS = 24 * 60 * 60_000;\n// The bound-thread reconcile walk reads a cursor + messages per binding; it\n// exists to catch results missed at run end, so it runs on a slow cadence off\n// the claim path rather than on every 1s tick.\nconst RECONCILE_INTERVAL_MS = 30_000;\n\nfunction waitForAgentEndOrTimeout(agentEnd: Promise<void>, timeoutMs: number): Promise<boolean> {\n return new Promise(resolve => {\n const timeout = setTimeout(() => resolve(false), timeoutMs);\n timeout.unref?.();\n void agentEnd.then(() => {\n clearTimeout(timeout);\n resolve(true);\n });\n });\n}\n\ninterface ThreadSwitchSession {\n thread: {\n switch(input: { threadId: string }): Promise<unknown>;\n };\n}\n\ninterface FactoryNotificationResult {\n persisted?: Promise<unknown>;\n accepted?: Promise<{\n action?: string;\n output?: { consumeStream(): Promise<unknown> };\n }>;\n}\n\ninterface DispatcherSession extends SkillSession {\n thread: {\n switch(input: { threadId: string }): Promise<unknown>;\n listActiveMessages(): Promise<Array<{ id: string }>>;\n };\n abort(): void;\n sendSignal(\n input: { id: string; type: 'user'; tagName: 'user'; contents: string },\n options: { requestContext: RequestContext; requireDelivery?: boolean },\n ): { accepted: Promise<{ accepted: true; runId?: string; action?: string }> };\n subscribe(listener: AgentControllerEventListener): () => void;\n}\n\ntype FactoryController = Pick<AgentController<MastraCodeState>, 'getSessionByResource'>;\ntype BoundDispatcherSession = Session<MastraCodeState>;\n\nexport interface FactoryBindingPreparationInput {\n record: FactoryDeferredDecisionRecord;\n item: WorkItemRow;\n role: string;\n}\n\nexport interface FactoryDecisionDispatcherOptions {\n controller: FactoryController;\n transitionService: Pick<FactoryTransitionService, 'transition'>;\n storage: WorkItemsStorage;\n ownerId?: string;\n /** `false` parks `invokeSkill` effects as `proposed`; every other effect still runs. */\n isAutoRunEnabled: (tenant: { orgId: string; factoryProjectId: string }) => Promise<boolean>;\n reconcileToolResults?: () => Promise<void>;\n prepareBinding?: (input: FactoryBindingPreparationInput) => Promise<void>;\n primeCredentials?: (tenant: { orgId: string; userId: string }) => Promise<void>;\n /** Injects the work item's recent comments into skill-invocation kickoffs. */\n feedReader?: FactoryFeedReader;\n resolveLinkedWorkItemParentId?: (input: {\n orgId: string;\n decision: Extract<FactoryCommitDecision, { type: 'upsertLinkedWorkItem' }>;\n }) => Promise<string | null>;\n maxInFlight?: number;\n /** How often the stale-binding sweep runs. Defaults to 10 minutes. */\n staleBindingSweepIntervalMs?: number;\n /** Active bindings older than this are revoked by the sweep. Defaults to 24 hours. */\n staleBindingTtlMs?: number;\n /** How often the bound-thread reconcile walk runs. Defaults to 30 seconds. */\n reconcileIntervalMs?: number;\n /** How long to wait for a run's terminal event before failing for retry. Defaults to 10 minutes. */\n skillCompletionObservationTimeoutMs?: number;\n}\n\nfunction positiveMs(value: number | undefined, fallback: number): number {\n return typeof value === 'number' && Number.isFinite(value) && value > 0 ? Math.floor(value) : fallback;\n}\n\nfunction sanitizeDispatchError(error: unknown): string {\n const message = error instanceof Error ? error.message : String(error);\n return message\n .replace(/\\b(?:bearer|token|api[-_ ]?key|authorization)\\s*[:=]?\\s*[^\\s,;]+/gi, '[redacted]')\n .slice(0, MAX_ERROR_LENGTH);\n}\n\nfunction retryAt(now: Date, attempts: number): Date {\n return new Date(now.getTime() + Math.min(1_000 * 2 ** Math.max(0, attempts - 1), MAX_BACKOFF_MS));\n}\n\nfunction externalSourceForDecision(decision: Extract<FactoryCommitDecision, { type: 'upsertLinkedWorkItem' }>) {\n const [integrationId, type] =\n decision.source === 'github-pr'\n ? ['github', 'pull-request']\n : decision.source === 'github-issue'\n ? ['github', 'issue']\n : decision.source === 'linear-issue'\n ? ['linear', 'issue']\n : ['factory', 'manual'];\n return { integrationId, type, externalId: decision.sourceKey, url: decision.url ?? undefined };\n}\n\nfunction deferredActor(record: FactoryDeferredDecisionRecord): FactoryRuleActor {\n const actor = record.actor;\n if (\n actor?.type === 'github' &&\n typeof actor.login === 'string' &&\n typeof actor.trusted === 'boolean' &&\n typeof actor.factoryAuthored === 'boolean'\n ) {\n return {\n type: 'github',\n login: actor.login,\n trusted: actor.trusted,\n factoryAuthored: actor.factoryAuthored,\n };\n }\n return { type: 'system', id: 'factory-rule-dispatcher' };\n}\n\nfunction externalActor(actor: FactoryDeferredDecisionRecord['actor']): boolean {\n return actor !== null && actor.type !== 'human' && actor.type !== 'agent' && actor.type !== 'system';\n}\n\n/** A run start asks for consent; an external event asks before pulling a card back into a working lane. */\nfunction requestsConsent(record: FactoryDeferredDecisionRecord, decision: FactoryCommitDecision): boolean {\n if (decision.type === 'invokeSkill') return true;\n return decision.type === 'transition' && isWorkingFactoryRuleStage(decision.stage) && externalActor(record.actor);\n}\n\nfunction leaseIdentity(\n record: Pick<FactoryDeferredDecisionRecord | FactoryPendingStartRecord, 'id' | 'orgId' | 'factoryProjectId'>,\n ownerId: string,\n) {\n return { id: record.id, orgId: record.orgId, factoryProjectId: record.factoryProjectId, ownerId };\n}\n\nasync function awaitNotification(\n send: () => Promise<FactoryNotificationResult>,\n requireDelivery = false,\n): Promise<{ action?: string } | undefined> {\n try {\n const notification = await send();\n const [, accepted] = await Promise.all([notification.persisted, notification.accepted]);\n if (!accepted) {\n if (requireDelivery) {\n throw new FactoryDispatchError(\n 'notification_delivery_failed',\n 'Factory notification was persisted without agent delivery.',\n );\n }\n return undefined;\n }\n if (!requireDelivery) return accepted;\n if (accepted.action === 'wake') {\n if (!accepted.output) {\n throw new FactoryDispatchError('notification_delivery_failed', 'Factory notification wake had no output.');\n }\n await accepted.output.consumeStream();\n return accepted;\n }\n if (accepted.action !== 'deliver') {\n throw new FactoryDispatchError(\n 'notification_delivery_failed',\n `Factory notification did not reach the agent (${String(accepted.action)}).`,\n );\n }\n return accepted;\n } catch (error) {\n if (error instanceof FactoryDispatchError) throw error;\n throw new FactoryDispatchError(\n 'notification_delivery_failed',\n `Factory notification delivery failed: ${sanitizeDispatchError(error)}`,\n { cause: error },\n );\n }\n}\n\nexport class FactoryDecisionDispatcher {\n readonly #controller: FactoryController;\n readonly #transitionService: Pick<FactoryTransitionService, 'transition'>;\n readonly #storage: WorkItemsStorage;\n readonly #ownerId: string;\n readonly #isAutoRunEnabled: (tenant: { orgId: string; factoryProjectId: string }) => Promise<boolean>;\n readonly #reconcileToolResults?: () => Promise<void>;\n readonly #prepareBinding?: (input: FactoryBindingPreparationInput) => Promise<void>;\n readonly #primeCredentials?: (tenant: { orgId: string; userId: string }) => Promise<void>;\n readonly #feedReader?: FactoryFeedReader;\n readonly #resolveLinkedWorkItemParentId?: FactoryDecisionDispatcherOptions['resolveLinkedWorkItemParentId'];\n readonly #maxInFlight: number;\n readonly #staleBindingSweepIntervalMs: number;\n readonly #staleBindingTtlMs: number;\n #lastStaleBindingSweepAt?: Date;\n readonly #reconcileIntervalMs: number;\n readonly #skillCompletionObservationTimeoutMs: number;\n #lastReconcileAt?: Date;\n #reconcileInFlight?: Promise<void>;\n #timer?: ReturnType<typeof setInterval>;\n #activeClaim?: Promise<void>;\n readonly #inFlight = new Set<Promise<void>>();\n\n constructor(options: FactoryDecisionDispatcherOptions) {\n this.#controller = options.controller;\n this.#transitionService = options.transitionService;\n this.#storage = options.storage;\n this.#ownerId = options.ownerId ?? `factory-dispatcher:${randomUUID()}`;\n this.#isAutoRunEnabled = options.isAutoRunEnabled;\n this.#reconcileToolResults = options.reconcileToolResults;\n this.#prepareBinding = options.prepareBinding;\n this.#primeCredentials = options.primeCredentials;\n this.#feedReader = options.feedReader;\n this.#resolveLinkedWorkItemParentId = options.resolveLinkedWorkItemParentId;\n const maxInFlight = options.maxInFlight ?? MAX_IN_FLIGHT;\n this.#maxInFlight = Number.isFinite(maxInFlight) && maxInFlight > 0 ? Math.floor(maxInFlight) : MAX_IN_FLIGHT;\n this.#staleBindingSweepIntervalMs = positiveMs(\n options.staleBindingSweepIntervalMs,\n STALE_BINDING_SWEEP_INTERVAL_MS,\n );\n this.#staleBindingTtlMs = positiveMs(options.staleBindingTtlMs, STALE_BINDING_TTL_MS);\n this.#reconcileIntervalMs = positiveMs(options.reconcileIntervalMs, RECONCILE_INTERVAL_MS);\n this.#skillCompletionObservationTimeoutMs = positiveMs(\n options.skillCompletionObservationTimeoutMs,\n SKILL_COMPLETION_OBSERVATION_TIMEOUT_MS,\n );\n }\n\n start(): void {\n if (this.#timer) return;\n void this.#tick();\n this.#timer = setInterval(() => void this.#tick(), POLL_MS);\n this.#timer.unref?.();\n }\n\n async stop(): Promise<void> {\n if (this.#timer) clearInterval(this.#timer);\n this.#timer = undefined;\n await this.#activeClaim;\n await Promise.allSettled([...this.#inFlight]);\n }\n\n async runOnce(now = new Date()): Promise<void> {\n await Promise.all(await this.#claimAndStart(now));\n }\n\n /**\n * Claims a batch and starts dispatches without awaiting their completion.\n * Dispatches can legitimately take minutes (skill kickoffs consume the\n * agent's run stream; binding preparation provisions sandboxes), so awaiting\n * them here would freeze the poll loop and starve every other queued\n * decision. In-flight records stay protected from re-claim by lease renewal.\n */\n async #claimAndStart(now: Date): Promise<Array<Promise<void>>> {\n // Fire-and-forget like the reconcile walk: the sweep reads every active\n // binding, so awaiting it would stretch the tick as the active set grows.\n void this.#maybeSweepStaleBindings(now);\n this.#maybeReconcileToolResults(now);\n const capacity = this.#maxInFlight - this.#inFlight.size;\n if (capacity <= 0) return [];\n const limit = Math.min(BATCH_SIZE, capacity);\n const leaseExpiresAt = new Date(now.getTime() + LEASE_MS);\n // Starts are claimed before deferred decisions: a pending start is a user\n // waiting on a brand-new session, while a deferred decision is a background\n // continuation of one that is already running. A deep decision queue must\n // never starve new sessions out of the tick.\n const starts = await this.#storage.claimPendingStarts({\n ownerId: this.#ownerId,\n now,\n leaseExpiresAt,\n limit,\n });\n const decisionsLimit = limit - starts.length;\n const decisions =\n decisionsLimit > 0\n ? await this.#storage.claimDeferredDecisions({\n ownerId: this.#ownerId,\n now,\n leaseExpiresAt,\n limit: decisionsLimit,\n })\n : [];\n return [\n ...starts.map(start => this.#track(this.#dispatchPendingStart(start, now))),\n ...decisions.map(decision => this.#track(this.#dispatchDecision(decision, now))),\n ];\n }\n\n /**\n * Throttled, coalesced, non-blocking bound-thread reconcile: dispatch\n * claiming never waits behind cursor + message reads, and overlapping runs\n * are skipped while one is still in flight.\n */\n #maybeReconcileToolResults(now: Date): void {\n if (!this.#reconcileToolResults || this.#reconcileInFlight) return;\n if (this.#lastReconcileAt && now.getTime() - this.#lastReconcileAt.getTime() < this.#reconcileIntervalMs) return;\n this.#lastReconcileAt = now;\n const run = this.#reconcileToolResults()\n .catch(error => {\n console.error('Factory tool-result reconcile failed', sanitizeDispatchError(error));\n })\n .finally(() => {\n this.#reconcileInFlight = undefined;\n });\n this.#reconcileInFlight = run;\n this.#track(run);\n }\n\n /** Slow-cadence revocation of leaked/legacy bindings; failures never block the claim path. */\n async #maybeSweepStaleBindings(now: Date): Promise<void> {\n // The first tick only anchors the cadence: sweeping at boot would race the\n // startup reconcile that is still draining trailing tool results.\n if (!this.#lastStaleBindingSweepAt) {\n this.#lastStaleBindingSweepAt = now;\n return;\n }\n if (now.getTime() - this.#lastStaleBindingSweepAt.getTime() < this.#staleBindingSweepIntervalMs) return;\n this.#lastStaleBindingSweepAt = now;\n try {\n const revoked = await this.#storage.revokeStaleRunBindings({\n olderThan: new Date(now.getTime() - this.#staleBindingTtlMs),\n now,\n });\n if (revoked > 0) console.info(`Factory stale-binding sweep revoked ${revoked} binding(s)`);\n } catch (error) {\n console.error('Factory stale-binding sweep failed', sanitizeDispatchError(error));\n }\n }\n\n #track(dispatch: Promise<void>): Promise<void> {\n this.#inFlight.add(dispatch);\n void dispatch.catch(() => {}).then(() => this.#inFlight.delete(dispatch));\n return dispatch;\n }\n\n async #tick(): Promise<void> {\n if (this.#activeClaim) return;\n this.#activeClaim = this.#claimAndStart(new Date()).then(\n dispatches => {\n for (const dispatch of dispatches) {\n dispatch.catch(error => {\n console.error('Factory decision dispatch failed', sanitizeDispatchError(error));\n });\n }\n },\n error => {\n console.error('Factory decision dispatch cycle failed', sanitizeDispatchError(error));\n },\n );\n try {\n await this.#activeClaim;\n } finally {\n this.#activeClaim = undefined;\n }\n }\n\n async #dispatchDecision(record: FactoryDeferredDecisionRecord, now: Date): Promise<void> {\n let executionCompleted = false;\n try {\n const decision = validateFactoryRuleDecision(record.decision, record.causalChain.length);\n if (decision.type === 'reject') throw new Error('Deferred Factory decisions cannot reject.');\n if (await this.#needsApproval(record, decision)) {\n const proposed = await this.#storage.proposeDeferredDecision(leaseIdentity(record, this.#ownerId), new Date());\n if (!proposed) throw new Error('Factory decision lease was lost before approval could be requested.');\n return;\n }\n await this.#supersedeProposals(record, decision);\n await this.#withLease(\n async leaseExpiresAt =>\n this.#storage.renewDeferredDecisionLease(leaseIdentity(record, this.#ownerId), leaseExpiresAt),\n async () => this.#executeDecision(record, decision),\n );\n executionCompleted = true;\n const completed = await this.#storage.completeDeferredDecision(leaseIdentity(record, this.#ownerId), new Date());\n if (!completed) throw new Error('Factory decision lease was lost before completion.');\n } catch (error) {\n const terminal = record.attempts >= MAX_ATTEMPTS;\n await this.#storage.failDeferredDecision({\n ...leaseIdentity(record, this.#ownerId),\n now: new Date(),\n availableAt: retryAt(now, record.attempts),\n lastError: sanitizeDispatchError(error),\n failureCode: factoryDispatchFailureCode(error),\n terminal,\n advanceDeliveryGeneration: !executionCompleted,\n });\n }\n }\n\n /**\n * A proposal is a question: \"should this run start?\" Once that run is\n * starting anyway — because a person approved a later copy, or armed the item\n * — the question has been answered and the card must stop asking it. Left\n * alone the badge outlives the work it describes, and the one affordance that\n * means \"the loop is stopped, answer this\" cries wolf.\n */\n async #supersedeProposals(record: FactoryDeferredDecisionRecord, decision: FactoryCommitDecision): Promise<void> {\n if (decision.type !== 'invokeSkill' || !record.workItemId) return;\n try {\n await this.#storage.supersedeDecisionsForWorkItem({\n orgId: record.orgId,\n factoryProjectId: record.factoryProjectId,\n workItemId: record.workItemId,\n role: decision.role,\n supersededAt: new Date(),\n });\n } catch (error) {\n // Best-effort: a stale badge is not worth failing the run it describes.\n console.error('Factory proposal supersede failed', sanitizeDispatchError(error));\n }\n }\n\n // Effects a person owns: starting a run (compute + code execution), and an\n // external event pulling a card back into a working lane.\n async #needsApproval(record: FactoryDeferredDecisionRecord, decision: FactoryCommitDecision): Promise<boolean> {\n if (record.approvedAt !== null || !requestsConsent(record, decision)) return false;\n // Withholding auto-run decides what the Factory may pick up on its own, not\n // whether it may finish work a person already handed it. Once someone starts\n // an item, the runs that carry it to review are that same request continuing.\n const item = record.workItemId ? await this.#storage.get({ orgId: record.orgId, id: record.workItemId }) : null;\n // Neither arming nor auto-run is standing consent for code from outside the\n // write-access circle: only the run a person's own gesture queued is pre-approved.\n if (item && externallyAuthoredWorkItem(item)) return true;\n if (item?.autonomyArmedAt != null) return false;\n return !(await this.#isAutoRunEnabled({ orgId: record.orgId, factoryProjectId: record.factoryProjectId }));\n }\n\n async #executeDecision(record: FactoryDeferredDecisionRecord, decision: FactoryCommitDecision): Promise<void> {\n const nextChain: FactoryRuleCausalEntry[] = [\n ...(record.causalChain as FactoryRuleCausalEntry[]),\n { ingressId: record.idempotencyKey, decisionType: decision.type },\n ];\n if (nextChain.length > MAX_FACTORY_RULE_CAUSAL_DEPTH) throw new Error('Factory rule causal depth exceeded.');\n\n switch (decision.type) {\n case 'transition': {\n const item = await this.#requireItem(record);\n const result = await this.#transitionService.transition({\n orgId: record.orgId,\n factoryProjectId: record.factoryProjectId,\n workItemId: item.id,\n board: decision.board,\n stage: decision.stage,\n expectedRevision: item.revision,\n actor: { type: 'system', id: 'factory-rule-dispatcher' },\n ingress: { type: 'rule', identity: `decision:${record.idempotencyKey}` },\n cause: 'rule_decision',\n causalChain: nextChain,\n ...(decision.reenter ? { reenter: true } : {}),\n });\n if (result.status === 'rejected') throw new Error(`${result.code}: ${result.reason}`);\n const transitionMessage = decision.message;\n if (!transitionMessage) return;\n // Best-effort recipient lookup: no active binding (or no authenticated\n // session owner) means nobody is engaged with this item, so the\n // transition itself is the whole effect. A retry after a delivery\n // failure is safe because the transition replays by ingress identity.\n const binding = await this.#findBinding(record, transitionMessage.role);\n if (!binding) return;\n const startedBy = item.sessions[binding.role]?.startedBy;\n if (!startedBy) return;\n await this.#primeCredentials?.({ orgId: record.orgId, userId: startedBy });\n const requestContext = new RequestContext();\n requestContext.set('user', { workosId: startedBy, organizationId: record.orgId });\n const session = await this.#findSession(binding);\n if (!session) return;\n await awaitNotification(\n () =>\n session.sendNotificationSignal(\n {\n source: 'factory',\n kind: 'rule-message',\n summary: transitionMessage.text,\n priority: 'high',\n payload: { message: transitionMessage.text },\n sourceId: record.id,\n dedupeKey: record.idempotencyKey,\n },\n {\n ifActive: { behavior: 'deliver' },\n ifIdle: { behavior: 'wake' },\n requestContext,\n },\n ),\n true,\n );\n return;\n }\n case 'upsertLinkedWorkItem': {\n await this.#upsertLinkedItem(record, decision, nextChain);\n return;\n }\n case 'invokeSkill': {\n const binding = await this.#requireOrPrepareBinding(record, decision.role);\n const item = record.workItemId ? await this.#storage.get({ orgId: record.orgId, id: record.workItemId }) : null;\n const startedBy = item?.sessions[binding.role]?.startedBy;\n if (!startedBy) throw new Error(`Factory binding ${binding.id} has no authenticated session owner.`);\n await this.#primeCredentials?.({ orgId: record.orgId, userId: startedBy });\n const requestContext = new RequestContext();\n requestContext.set('user', { workosId: startedBy, organizationId: record.orgId });\n const resolved =\n decision.skillName === undefined\n ? await resolvePromptInvocation(this.#controller, {\n resourceId: binding.resourceId,\n prompt: decision.prompt,\n })\n : await resolveSkillInvocation(this.#controller, {\n resourceId: binding.resourceId,\n name: decision.skillName,\n arguments: decision.arguments,\n });\n const session = resolved.session as DispatcherSession;\n await this.#switchThread(session, binding);\n const deliveryId =\n record.deliveryGeneration === 0 ? record.id : `${record.id}:retry:${record.deliveryGeneration}`;\n const delivered = await session.thread.listActiveMessages();\n if (delivered.some(message => message.id === deliveryId)) return;\n // Safe under the replay guard above: it matches deliveryId, never prompt content.\n const kickoffContents = await withWorkItemFeed(\n this.#feedReader,\n { orgId: record.orgId, factoryProjectId: record.factoryProjectId, workItemId: record.workItemId },\n resolved.message,\n );\n if (decision.cancelInFlight) session.abort();\n const precedingMessage = decision.precedingMessage;\n if (precedingMessage) {\n await awaitNotification(() =>\n session.sendNotificationSignal(\n {\n source: 'factory',\n kind: 'stage-transition',\n summary: precedingMessage,\n priority: 'medium',\n payload: { message: precedingMessage },\n sourceId: `${record.id}:stage-transition`,\n dedupeKey: `${record.idempotencyKey}:stage-transition`,\n },\n {\n ifActive: { behavior: 'deliver' },\n ifIdle: { behavior: 'persist' },\n requestContext,\n },\n ),\n );\n }\n let resolveAgentEnd!: () => void;\n let agentEnd!: Promise<void>;\n // The run's own verdict, not the delivery's. A signal can reach the\n // agent perfectly and the run still die on a provider error or be\n // cancelled mid-flight; without this the decision reports success and\n // the break is invisible on the card.\n let endReason: 'complete' | 'aborted' | 'error' | 'suspended' | undefined;\n // Re-armed before a redelivery so the second send waits on its own run's\n // ending rather than seeing the one that already resolved.\n const armAgentEnd = () => {\n endReason = undefined;\n agentEnd = new Promise<void>(resolve => {\n resolveAgentEnd = resolve;\n });\n };\n armAgentEnd();\n const unsubscribe = session.subscribe(event => {\n if (event.type === 'agent_end') {\n endReason = event.reason;\n resolveAgentEnd();\n }\n });\n\n const sendKickoff = async () => {\n const result = session.sendSignal(\n {\n id: deliveryId,\n type: 'user',\n tagName: 'user',\n contents: kickoffContents,\n },\n // Without `requireDelivery` the session resolves `accepted` on the\n // next tick and swallows wake failures, so a kickoff that never\n // reached the agent would be marked succeeded and the thread would\n // stay empty forever.\n { requestContext, requireDelivery: true },\n );\n const settled = await result.accepted;\n if (settled.action !== 'wake' && settled.action !== 'deliver') {\n // An undefined action means the session did not verify delivery at\n // all — with `requireDelivery` set that is a contract violation, not\n // a success.\n throw new Error(`Factory skill invocation signal did not reach the agent (${String(settled.action)}).`);\n }\n return settled;\n };\n\n try {\n let settled = await sendKickoff();\n if (settled.action === 'deliver') {\n // `deliver` means the signal was queued onto a run that was already\n // in flight. If that run ends before draining its queue the prompt\n // is dropped silently: no turn starts, no error surfaces, and the\n // decision reports success while the card sits in its new stage with\n // nobody working. Signals persist under their generation-scoped id\n // (the same identity the replay guard above reads), so confirm the\n // message actually landed in the thread rather than trusting the ack.\n const landed = await session.thread.listActiveMessages();\n if (!landed.some(message => message.id === deliveryId)) {\n // The condition that resolves this is the in-flight run ending, so\n // wait for exactly that and redeliver into the idle session. A\n // backoff cannot work here: retries are sized in seconds and a turn\n // takes minutes, so every attempt lands on the same busy run and\n // the card burns its whole budget without the session ever having\n // had a chance to be free.\n if (!(await waitForAgentEndOrTimeout(agentEnd, this.#skillCompletionObservationTimeoutMs))) {\n throw new Error('Factory skill invocation is waiting on a run that has not ended.');\n }\n armAgentEnd();\n settled = await sendKickoff();\n if (settled.action !== 'wake') {\n throw new Error('Factory skill invocation was queued onto an ending run and never reached the agent.');\n }\n }\n }\n // A landed `deliver` still runs on the in-flight session, so the run's\n // terminal outcome matters as much as a fresh wake's: a run that ends\n // in error after accepting the prompt has still failed this decision.\n {\n const observed = await waitForAgentEndOrTimeout(agentEnd, this.#skillCompletionObservationTimeoutMs);\n if (!observed) {\n // A completed decision with no observed run end is exactly the\n // silent-stall failure mode: the card advances while nobody\n // works it. Fail non-terminally so the attempts/backoff\n // machinery redelivers — the delivery generation guarantees the\n // retry sends a fresh kickoff instead of hitting the replay\n // guard.\n throw new Error('Factory skill run terminal event was not observed before timeout.');\n } else if (endReason === 'error') {\n throw new Error('Factory skill run ended in error.');\n } else if (endReason === 'aborted') {\n // Retryable, though an abort reads as deliberate. The stream does\n // not say who aborted, and in practice the dominant cause is the\n // process going away underneath the run — an operator restarting\n // the server — not anyone deciding this work should stop. Treating\n // that as terminal dead-ends the card at attempt 1 with nothing on\n // the board to press. A spurious retry is bounded by MAX_ATTEMPTS;\n // a dead card costs a human a manual nudge.\n throw new Error('Factory skill run was aborted before it finished.');\n }\n }\n } finally {\n unsubscribe();\n }\n return;\n }\n case 'sendMessage': {\n const binding = await this.#messageBinding(record, decision);\n // Nobody live on the card means nobody to tell, not a failure to retry.\n if (!binding) return;\n const item = record.workItemId ? await this.#storage.get({ orgId: record.orgId, id: record.workItemId }) : null;\n const startedBy = item?.sessions[binding.role]?.startedBy;\n if (!startedBy) throw new Error(`Factory binding ${binding.id} has no authenticated session owner.`);\n await this.#primeCredentials?.({ orgId: record.orgId, userId: startedBy });\n const requestContext = new RequestContext();\n requestContext.set('user', { workosId: startedBy, organizationId: record.orgId });\n const session = await this.#requireSession(binding);\n await awaitNotification(\n () =>\n session.sendNotificationSignal(\n {\n source: 'factory',\n kind: 'rule-message',\n summary: decision.message,\n priority: decision.priority ?? 'high',\n payload: { message: decision.message },\n sourceId: record.id,\n dedupeKey: record.idempotencyKey,\n },\n {\n ifActive: { behavior: 'deliver' },\n ifIdle: { behavior: decision.idleBehavior ?? 'wake' },\n requestContext,\n },\n ),\n true,\n );\n return;\n }\n case 'notify': {\n const binding = await this.#requireBinding(record);\n const session = await this.#requireSession(binding);\n await awaitNotification(() =>\n session.sendNotificationSignal({\n source: 'factory',\n kind: 'rule-notification',\n summary: decision.title,\n payload: { body: decision.body, level: decision.level },\n sourceId: record.id,\n dedupeKey: record.idempotencyKey,\n }),\n );\n }\n }\n }\n\n async #upsertLinkedItem(\n record: FactoryDeferredDecisionRecord,\n decision: Extract<FactoryCommitDecision, { type: 'upsertLinkedWorkItem' }>,\n causalChain: FactoryRuleCausalEntry[],\n ): Promise<void> {\n const parentWorkItemId =\n record.workItemId ??\n (await this.#resolveLinkedWorkItemParentId?.({\n orgId: record.orgId,\n decision,\n })) ??\n null;\n let result = await this.#storage.upsert({\n orgId: record.orgId,\n userId: 'factory-rule-dispatcher',\n factoryProjectId: record.factoryProjectId,\n input: {\n externalSource: externalSourceForDecision(decision),\n parentWorkItemId,\n title: decision.title,\n stages: ['intake'],\n sessions: {},\n metadata: { ...decision.metadata, [FACTORY_RULE_MATERIALIZATION_KEY]: record.idempotencyKey },\n },\n reuseMode: 'preserve',\n });\n if (!result.item.parentWorkItemId && parentWorkItemId) {\n const item = await this.#storage.setParentWorkItemIfMissing({\n orgId: record.orgId,\n id: result.item.id,\n userId: 'factory-rule-dispatcher',\n parentWorkItemId,\n });\n if (item) result = { ...result, item };\n }\n const materializedByDecision = result.item.metadata?.[FACTORY_RULE_MATERIALIZATION_KEY] === record.idempotencyKey;\n if (!materializedByDecision && (decision.stage === 'intake' || !result.item.stages.includes('intake'))) return;\n\n const board = decision.board;\n let expectedRevision = result.item.revision;\n if (materializedByDecision) {\n const initial = await this.#transitionService.transition({\n orgId: record.orgId,\n factoryProjectId: record.factoryProjectId,\n workItemId: result.item.id,\n board,\n stage: 'intake',\n expectedRevision,\n actor: deferredActor(record),\n ingress: { type: 'rule', identity: `decision:${record.idempotencyKey}:${result.item.id}:initial-entry` },\n cause: 'linked_item_materialized',\n causalChain,\n initialEntry: true,\n });\n if (initial.status === 'rejected') {\n if (result.created) await this.#storage.delete({ orgId: record.orgId, id: result.item.id });\n throw new Error(`${initial.code}: ${initial.reason}`);\n }\n expectedRevision = initial.revision;\n }\n if (decision.stage === 'intake') return;\n\n const moved = await this.#transitionService.transition({\n orgId: record.orgId,\n factoryProjectId: record.factoryProjectId,\n workItemId: result.item.id,\n board,\n stage: decision.stage,\n expectedRevision,\n actor: { type: 'system', id: 'factory-rule-dispatcher' },\n ingress: { type: 'rule', identity: `decision:${record.idempotencyKey}:${result.item.id}:destination` },\n cause: materializedByDecision ? 'linked_item_materialized' : 'linked_item_reconciled',\n causalChain,\n });\n if (moved.status === 'rejected') throw new Error(`${moved.code}: ${moved.reason}`);\n }\n\n async #requireItem(record: FactoryDeferredDecisionRecord) {\n if (!record.workItemId) throw new Error('Factory decision is not linked to a work item.');\n const item = await this.#storage.get({ orgId: record.orgId, id: record.workItemId });\n if (!item) throw new Error('Factory work item not found.');\n return item;\n }\n\n async #findBinding(\n record: FactoryDeferredDecisionRecord,\n role?: string,\n ): Promise<FactoryRunBindingRecord | undefined> {\n if (!record.workItemId) throw new Error('Factory decision is not linked to a work item.');\n const bindings = await this.#storage.listRunBindings(record.orgId, record.factoryProjectId, record.workItemId);\n return bindings\n .filter(candidate => candidate.status === 'active' && (role === undefined || candidate.role === role))\n .sort((left, right) => {\n if (role === undefined && left.role === 'work' && right.role !== 'work') return -1;\n if (role === undefined && right.role === 'work' && left.role !== 'work') return 1;\n return right.createdAt.getTime() - left.createdAt.getTime() || left.id.localeCompare(right.id);\n })[0];\n }\n\n async #requireBinding(record: FactoryDeferredDecisionRecord, role?: string): Promise<FactoryRunBindingRecord> {\n const binding = await this.#findBinding(record, role);\n if (!binding) {\n throw new FactoryDispatchError(\n 'session_unavailable',\n role ? `No active Factory binding for role ${role}.` : 'No active Factory binding.',\n );\n }\n return binding;\n }\n\n async #messageBinding(\n record: FactoryDeferredDecisionRecord,\n decision: Extract<FactoryCommitDecision, { type: 'sendMessage' }>,\n ): Promise<FactoryRunBindingRecord | undefined> {\n if (decision.prepareBinding && decision.role !== undefined) {\n return this.#requireOrPrepareBinding(record, decision.role);\n }\n return this.#findBinding(record, decision.role);\n }\n\n async #requireOrPrepareBinding(\n record: FactoryDeferredDecisionRecord,\n role: string,\n ): Promise<FactoryRunBindingRecord> {\n const binding = await this.#findBinding(record, role);\n if (binding) {\n const session = await this.#controller.getSessionByResource(binding.resourceId);\n if (session) return binding;\n }\n if (!this.#prepareBinding) {\n throw new FactoryDispatchError(\n 'session_unavailable',\n binding ? 'Bound Factory session not found.' : `No active Factory binding for role ${role}.`,\n );\n }\n const item = await this.#requireItem(record);\n await this.#prepareBinding({ record, item, role });\n return this.#requireBinding(record, role);\n }\n\n async #findSession(binding: FactoryRunBindingRecord): Promise<BoundDispatcherSession | undefined> {\n const session = await this.#controller.getSessionByResource(binding.resourceId);\n if (!session) return undefined;\n await this.#switchThread(session, binding);\n return session;\n }\n\n async #requireSession(binding: FactoryRunBindingRecord): Promise<BoundDispatcherSession> {\n const session = await this.#findSession(binding);\n if (!session) throw new FactoryDispatchError('session_unavailable', 'Bound Factory session not found.');\n return session;\n }\n\n async #switchThread(session: ThreadSwitchSession, binding: FactoryRunBindingRecord): Promise<void> {\n await session.thread.switch({ threadId: binding.threadId });\n }\n\n async #withLease(\n renew: (leaseExpiresAt: Date) => Promise<unknown | null>,\n effect: () => Promise<void>,\n ): Promise<void> {\n let renewalFailure: unknown;\n let renewal = Promise.resolve();\n const timer = setInterval(\n () => {\n renewal = renewal.then(async () => {\n try {\n const renewed = await renew(new Date(Date.now() + LEASE_MS));\n if (!renewed) renewalFailure = new Error('Factory dispatch lease was lost during execution.');\n } catch (error) {\n renewalFailure = error;\n }\n });\n },\n Math.floor(LEASE_MS / 3),\n );\n timer.unref?.();\n try {\n await effect();\n await renewal;\n if (renewalFailure) throw renewalFailure;\n } finally {\n clearInterval(timer);\n await renewal;\n }\n }\n\n async #dispatchPendingStart(record: FactoryPendingStartRecord, now: Date): Promise<void> {\n try {\n await this.#withLease(\n async leaseExpiresAt =>\n this.#storage.renewPendingStartLease(leaseIdentity(record, this.#ownerId), leaseExpiresAt),\n async () => {\n if (record.message === null) return;\n const bindings = await this.#storage.listRunBindings(record.orgId, record.factoryProjectId);\n const binding = bindings.find(\n candidate => candidate.id === record.bindingId && candidate.status === 'active',\n );\n if (!binding) {\n throw new FactoryDispatchError(\n 'session_unavailable',\n 'Prepared Factory binding is unavailable or revoked.',\n );\n }\n // Wake runs build the Factory workspace, which requires the\n // authenticated session owner on the request context.\n const item = await this.#storage.get({ orgId: record.orgId, id: binding.workItemId });\n const startedBy = item?.sessions[binding.role]?.startedBy;\n if (!startedBy) throw new Error(`Factory binding ${binding.id} has no authenticated session owner.`);\n await this.#primeCredentials?.({ orgId: record.orgId, userId: startedBy });\n const requestContext = new RequestContext();\n requestContext.set('user', { workosId: startedBy, organizationId: record.orgId });\n const session = await this.#requireSession(binding);\n let resolveAgentEnd!: () => void;\n let agentEnd!: Promise<void>;\n // The run's own verdict, not the delivery's: a kickoff delivered\n // into a run that is already terminating is consumed without\n // execution, and completing the pending start on the delivery ack\n // alone strands the card with a success ledger entry.\n let endReason: 'complete' | 'aborted' | 'error' | 'suspended' | undefined;\n const armAgentEnd = () => {\n endReason = undefined;\n agentEnd = new Promise<void>(resolve => {\n resolveAgentEnd = resolve;\n });\n };\n armAgentEnd();\n const unsubscribe = session.subscribe(event => {\n if (event.type === 'agent_end') {\n endReason = event.reason;\n resolveAgentEnd();\n }\n });\n const sendKickoff = (dedupeKey: string) =>\n awaitNotification(\n () =>\n session.sendNotificationSignal(\n {\n source: 'factory',\n kind: 'run-kickoff',\n summary: record.message!,\n priority: 'high',\n payload: { message: record.message },\n sourceId: record.id,\n dedupeKey,\n },\n { ifActive: { behavior: 'deliver' }, ifIdle: { behavior: 'wake' }, requestContext },\n ),\n true,\n );\n try {\n let settled = await sendKickoff(`factory-kickoff:${record.kickoffKey}`);\n if (settled?.action === 'deliver') {\n // `deliver` only proves the signal was queued onto a run already\n // in flight. If that run ends without draining its queue the\n // kickoff is dropped silently. There is no per-notification\n // \"processed\" signal, so wait for the in-flight run to end and\n // redeliver into the idle session unconditionally — the\n // generation-scoped dedupeKey defeats inbox dedupe and the\n // kickoff key keeps a duplicate run bounded, while a dropped\n // kickoff strands the card forever.\n if (!(await waitForAgentEndOrTimeout(agentEnd, this.#skillCompletionObservationTimeoutMs))) {\n throw new Error('Factory kickoff is waiting on a run that has not ended.');\n }\n armAgentEnd();\n settled = await sendKickoff(`factory-kickoff:${record.kickoffKey}:retry:${record.attempts}`);\n if (settled?.action !== 'wake') {\n throw new Error('Factory kickoff was queued onto an ending run and never reached the agent.');\n }\n }\n const observed = await waitForAgentEndOrTimeout(agentEnd, this.#skillCompletionObservationTimeoutMs);\n if (!observed) {\n throw new Error('Factory kickoff run terminal event was not observed before timeout.');\n } else if (endReason === 'error') {\n throw new Error('Factory kickoff run ended in error.');\n } else if (endReason === 'aborted') {\n // Retryable for the same reason as skill decisions: the dominant\n // cause is the process going away underneath the run, not a\n // deliberate stop, and a spurious retry is bounded by\n // MAX_ATTEMPTS while a dead card costs a human a manual nudge.\n throw new Error('Factory kickoff run was aborted before it finished.');\n }\n } finally {\n unsubscribe();\n }\n },\n );\n const completed = await this.#storage.completePendingStart(leaseIdentity(record, this.#ownerId), new Date());\n if (!completed) throw new Error('Factory kickoff lease was lost before completion.');\n } catch (error) {\n await this.#storage.failPendingStart({\n ...leaseIdentity(record, this.#ownerId),\n now: new Date(),\n availableAt: retryAt(now, record.attempts),\n lastError: sanitizeDispatchError(error),\n failureCode: factoryDispatchFailureCode(error),\n terminal: record.attempts >= MAX_ATTEMPTS,\n });\n }\n }\n}\n\nexport const FACTORY_DISPATCH_CONSTANTS = {\n leaseMs: LEASE_MS,\n pollMs: POLL_MS,\n batchSize: BATCH_SIZE,\n maxAttempts: MAX_ATTEMPTS,\n maxErrorLength: MAX_ERROR_LENGTH,\n maxBackoffMs: MAX_BACKOFF_MS,\n skillCompletionObservationTimeoutMs: SKILL_COMPLETION_OBSERVATION_TIMEOUT_MS,\n maxInFlight: MAX_IN_FLIGHT,\n stages: FACTORY_RULE_STAGES,\n} as const;\n"],"mappings":"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"}