@cat-factory/orchestration 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (182) hide show
  1. package/LICENSE +21 -0
  2. package/dist/container.d.ts +460 -0
  3. package/dist/container.d.ts.map +1 -0
  4. package/dist/container.js +657 -0
  5. package/dist/container.js.map +1 -0
  6. package/dist/index.d.ts +29 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +31 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/modules/board/BoardService.d.ts +125 -0
  11. package/dist/modules/board/BoardService.d.ts.map +1 -0
  12. package/dist/modules/board/BoardService.js +496 -0
  13. package/dist/modules/board/BoardService.js.map +1 -0
  14. package/dist/modules/board/board.logic.d.ts +17 -0
  15. package/dist/modules/board/board.logic.d.ts.map +1 -0
  16. package/dist/modules/board/board.logic.js +51 -0
  17. package/dist/modules/board/board.logic.js.map +1 -0
  18. package/dist/modules/boardScan/BoardScanService.d.ts +35 -0
  19. package/dist/modules/boardScan/BoardScanService.d.ts.map +1 -0
  20. package/dist/modules/boardScan/BoardScanService.js +91 -0
  21. package/dist/modules/boardScan/BoardScanService.js.map +1 -0
  22. package/dist/modules/boardScan/board-scan.logic.d.ts +10 -0
  23. package/dist/modules/boardScan/board-scan.logic.d.ts.map +1 -0
  24. package/dist/modules/boardScan/board-scan.logic.js +26 -0
  25. package/dist/modules/boardScan/board-scan.logic.js.map +1 -0
  26. package/dist/modules/bootstrap/BootstrapService.d.ts +114 -0
  27. package/dist/modules/bootstrap/BootstrapService.d.ts.map +1 -0
  28. package/dist/modules/bootstrap/BootstrapService.js +516 -0
  29. package/dist/modules/bootstrap/BootstrapService.js.map +1 -0
  30. package/dist/modules/clarity/ClarityReviewService.d.ts +48 -0
  31. package/dist/modules/clarity/ClarityReviewService.d.ts.map +1 -0
  32. package/dist/modules/clarity/ClarityReviewService.js +63 -0
  33. package/dist/modules/clarity/ClarityReviewService.js.map +1 -0
  34. package/dist/modules/clarity/clarity.logic.d.ts +36 -0
  35. package/dist/modules/clarity/clarity.logic.d.ts.map +1 -0
  36. package/dist/modules/clarity/clarity.logic.js +98 -0
  37. package/dist/modules/clarity/clarity.logic.js.map +1 -0
  38. package/dist/modules/estimation/estimate.logic.d.ts +11 -0
  39. package/dist/modules/estimation/estimate.logic.d.ts.map +1 -0
  40. package/dist/modules/estimation/estimate.logic.js +37 -0
  41. package/dist/modules/estimation/estimate.logic.js.map +1 -0
  42. package/dist/modules/execution/AgentContextBuilder.d.ts +114 -0
  43. package/dist/modules/execution/AgentContextBuilder.d.ts.map +1 -0
  44. package/dist/modules/execution/AgentContextBuilder.js +316 -0
  45. package/dist/modules/execution/AgentContextBuilder.js.map +1 -0
  46. package/dist/modules/execution/CompanionController.d.ts +60 -0
  47. package/dist/modules/execution/CompanionController.d.ts.map +1 -0
  48. package/dist/modules/execution/CompanionController.js +216 -0
  49. package/dist/modules/execution/CompanionController.js.map +1 -0
  50. package/dist/modules/execution/ExecutionService.d.ts +874 -0
  51. package/dist/modules/execution/ExecutionService.d.ts.map +1 -0
  52. package/dist/modules/execution/ExecutionService.js +2921 -0
  53. package/dist/modules/execution/ExecutionService.js.map +1 -0
  54. package/dist/modules/execution/MergeResolver.d.ts +34 -0
  55. package/dist/modules/execution/MergeResolver.d.ts.map +1 -0
  56. package/dist/modules/execution/MergeResolver.js +81 -0
  57. package/dist/modules/execution/MergeResolver.js.map +1 -0
  58. package/dist/modules/execution/ReviewGateController.d.ts +163 -0
  59. package/dist/modules/execution/ReviewGateController.d.ts.map +1 -0
  60. package/dist/modules/execution/ReviewGateController.js +251 -0
  61. package/dist/modules/execution/ReviewGateController.js.map +1 -0
  62. package/dist/modules/execution/TesterController.d.ts +61 -0
  63. package/dist/modules/execution/TesterController.d.ts.map +1 -0
  64. package/dist/modules/execution/TesterController.js +215 -0
  65. package/dist/modules/execution/TesterController.js.map +1 -0
  66. package/dist/modules/execution/advance.d.ts +84 -0
  67. package/dist/modules/execution/advance.d.ts.map +1 -0
  68. package/dist/modules/execution/advance.js +2 -0
  69. package/dist/modules/execution/advance.js.map +1 -0
  70. package/dist/modules/execution/artifact-review.logic.d.ts +25 -0
  71. package/dist/modules/execution/artifact-review.logic.d.ts.map +1 -0
  72. package/dist/modules/execution/artifact-review.logic.js +39 -0
  73. package/dist/modules/execution/artifact-review.logic.js.map +1 -0
  74. package/dist/modules/execution/ci.logic.d.ts +101 -0
  75. package/dist/modules/execution/ci.logic.d.ts.map +1 -0
  76. package/dist/modules/execution/ci.logic.js +117 -0
  77. package/dist/modules/execution/ci.logic.js.map +1 -0
  78. package/dist/modules/execution/drive.d.ts +47 -0
  79. package/dist/modules/execution/drive.d.ts.map +1 -0
  80. package/dist/modules/execution/drive.js +112 -0
  81. package/dist/modules/execution/drive.js.map +1 -0
  82. package/dist/modules/execution/gates.d.ts +97 -0
  83. package/dist/modules/execution/gates.d.ts.map +1 -0
  84. package/dist/modules/execution/gates.js +2 -0
  85. package/dist/modules/execution/gates.js.map +1 -0
  86. package/dist/modules/execution/individualVendors.logic.d.ts +22 -0
  87. package/dist/modules/execution/individualVendors.logic.d.ts.map +1 -0
  88. package/dist/modules/execution/individualVendors.logic.js +33 -0
  89. package/dist/modules/execution/individualVendors.logic.js.map +1 -0
  90. package/dist/modules/execution/job.logic.d.ts +52 -0
  91. package/dist/modules/execution/job.logic.d.ts.map +1 -0
  92. package/dist/modules/execution/job.logic.js +56 -0
  93. package/dist/modules/execution/job.logic.js.map +1 -0
  94. package/dist/modules/execution/release.logic.d.ts +43 -0
  95. package/dist/modules/execution/release.logic.d.ts.map +1 -0
  96. package/dist/modules/execution/release.logic.js +49 -0
  97. package/dist/modules/execution/release.logic.js.map +1 -0
  98. package/dist/modules/execution/retry.logic.d.ts +40 -0
  99. package/dist/modules/execution/retry.logic.d.ts.map +1 -0
  100. package/dist/modules/execution/retry.logic.js +83 -0
  101. package/dist/modules/execution/retry.logic.js.map +1 -0
  102. package/dist/modules/execution/stepGating.logic.d.ts +15 -0
  103. package/dist/modules/execution/stepGating.logic.d.ts.map +1 -0
  104. package/dist/modules/execution/stepGating.logic.js +29 -0
  105. package/dist/modules/execution/stepGating.logic.js.map +1 -0
  106. package/dist/modules/execution/stepResolvers.d.ts +41 -0
  107. package/dist/modules/execution/stepResolvers.d.ts.map +1 -0
  108. package/dist/modules/execution/stepResolvers.js +2 -0
  109. package/dist/modules/execution/stepResolvers.js.map +1 -0
  110. package/dist/modules/execution/tester-infra.logic.d.ts +42 -0
  111. package/dist/modules/execution/tester-infra.logic.d.ts.map +1 -0
  112. package/dist/modules/execution/tester-infra.logic.js +46 -0
  113. package/dist/modules/execution/tester-infra.logic.js.map +1 -0
  114. package/dist/modules/merge/MergePresetService.d.ts +32 -0
  115. package/dist/modules/merge/MergePresetService.d.ts.map +1 -0
  116. package/dist/modules/merge/MergePresetService.js +109 -0
  117. package/dist/modules/merge/MergePresetService.js.map +1 -0
  118. package/dist/modules/modelDefaults/ModelDefaultsService.d.ts +22 -0
  119. package/dist/modules/modelDefaults/ModelDefaultsService.d.ts.map +1 -0
  120. package/dist/modules/modelDefaults/ModelDefaultsService.js +28 -0
  121. package/dist/modules/modelDefaults/ModelDefaultsService.js.map +1 -0
  122. package/dist/modules/notifications/NotificationService.d.ts +74 -0
  123. package/dist/modules/notifications/NotificationService.d.ts.map +1 -0
  124. package/dist/modules/notifications/NotificationService.js +131 -0
  125. package/dist/modules/notifications/NotificationService.js.map +1 -0
  126. package/dist/modules/observability/LlmObservabilityService.d.ts +121 -0
  127. package/dist/modules/observability/LlmObservabilityService.d.ts.map +1 -0
  128. package/dist/modules/observability/LlmObservabilityService.js +140 -0
  129. package/dist/modules/observability/LlmObservabilityService.js.map +1 -0
  130. package/dist/modules/observability/observability.logic.d.ts +57 -0
  131. package/dist/modules/observability/observability.logic.d.ts.map +1 -0
  132. package/dist/modules/observability/observability.logic.js +186 -0
  133. package/dist/modules/observability/observability.logic.js.map +1 -0
  134. package/dist/modules/pipelines/PipelineService.d.ts +54 -0
  135. package/dist/modules/pipelines/PipelineService.d.ts.map +1 -0
  136. package/dist/modules/pipelines/PipelineService.js +226 -0
  137. package/dist/modules/pipelines/PipelineService.js.map +1 -0
  138. package/dist/modules/pipelines/pipelineShape.d.ts +53 -0
  139. package/dist/modules/pipelines/pipelineShape.d.ts.map +1 -0
  140. package/dist/modules/pipelines/pipelineShape.js +74 -0
  141. package/dist/modules/pipelines/pipelineShape.js.map +1 -0
  142. package/dist/modules/recurring/RecurringPipelineService.d.ts +76 -0
  143. package/dist/modules/recurring/RecurringPipelineService.d.ts.map +1 -0
  144. package/dist/modules/recurring/RecurringPipelineService.js +295 -0
  145. package/dist/modules/recurring/RecurringPipelineService.js.map +1 -0
  146. package/dist/modules/recurring/TrackerSettingsService.d.ts +16 -0
  147. package/dist/modules/recurring/TrackerSettingsService.d.ts.map +1 -0
  148. package/dist/modules/recurring/TrackerSettingsService.js +30 -0
  149. package/dist/modules/recurring/TrackerSettingsService.js.map +1 -0
  150. package/dist/modules/recurring/schedule.logic.d.ts +14 -0
  151. package/dist/modules/recurring/schedule.logic.d.ts.map +1 -0
  152. package/dist/modules/recurring/schedule.logic.js +85 -0
  153. package/dist/modules/recurring/schedule.logic.js.map +1 -0
  154. package/dist/modules/releaseHealth/ReleaseHealthService.d.ts +38 -0
  155. package/dist/modules/releaseHealth/ReleaseHealthService.d.ts.map +1 -0
  156. package/dist/modules/releaseHealth/ReleaseHealthService.js +96 -0
  157. package/dist/modules/releaseHealth/ReleaseHealthService.js.map +1 -0
  158. package/dist/modules/requirements/RequirementReviewService.d.ts +48 -0
  159. package/dist/modules/requirements/RequirementReviewService.d.ts.map +1 -0
  160. package/dist/modules/requirements/RequirementReviewService.js +83 -0
  161. package/dist/modules/requirements/RequirementReviewService.js.map +1 -0
  162. package/dist/modules/requirements/requirements.logic.d.ts +93 -0
  163. package/dist/modules/requirements/requirements.logic.d.ts.map +1 -0
  164. package/dist/modules/requirements/requirements.logic.js +203 -0
  165. package/dist/modules/requirements/requirements.logic.js.map +1 -0
  166. package/dist/modules/review/IterativeReviewService.d.ts +175 -0
  167. package/dist/modules/review/IterativeReviewService.d.ts.map +1 -0
  168. package/dist/modules/review/IterativeReviewService.js +327 -0
  169. package/dist/modules/review/IterativeReviewService.js.map +1 -0
  170. package/dist/modules/serviceFragmentDefaults/ServiceFragmentDefaultsService.d.ts +20 -0
  171. package/dist/modules/serviceFragmentDefaults/ServiceFragmentDefaultsService.d.ts.map +1 -0
  172. package/dist/modules/serviceFragmentDefaults/ServiceFragmentDefaultsService.js +26 -0
  173. package/dist/modules/serviceFragmentDefaults/ServiceFragmentDefaultsService.js.map +1 -0
  174. package/dist/modules/services/ServiceMountService.d.ts +48 -0
  175. package/dist/modules/services/ServiceMountService.d.ts.map +1 -0
  176. package/dist/modules/services/ServiceMountService.js +90 -0
  177. package/dist/modules/services/ServiceMountService.js.map +1 -0
  178. package/dist/modules/settings/WorkspaceSettingsService.d.ts +22 -0
  179. package/dist/modules/settings/WorkspaceSettingsService.d.ts.map +1 -0
  180. package/dist/modules/settings/WorkspaceSettingsService.js +50 -0
  181. package/dist/modules/settings/WorkspaceSettingsService.js.map +1 -0
  182. package/package.json +41 -0
@@ -0,0 +1,874 @@
1
+ import type { AgentFailureKind, Block, BlueprintService, CiStatusProvider, ExecutionInstance, MergePresetRepository, PullRequestMerger, PullRequestMergeabilityProvider, ReleaseHealthProvider, IncidentEnrichmentProvider, StepReviewComment, SubscriptionActivationRepository, TicketTrackerProvider } from '@cat-factory/kernel';
2
+ import { type HasPersonalSubscription } from './individualVendors.logic.js';
3
+ import { type ProviderCapabilities, type SubscriptionVendor } from '@cat-factory/kernel';
4
+ import type { NotificationService } from '../notifications/NotificationService.js';
5
+ import type { WorkspaceSettingsService } from '../settings/WorkspaceSettingsService.js';
6
+ import type { RequirementReviewService } from '../requirements/RequirementReviewService.js';
7
+ import type { ClarityReviewService } from '../clarity/ClarityReviewService.js';
8
+ import type { IterationCapChoice, RequirementReview, ClarityReview, ResolveRequirementsExceededChoice } from '@cat-factory/kernel';
9
+ import type { LlmObservabilityService } from '../observability/LlmObservabilityService.js';
10
+ import type { AccountRepository, BlockRepository, ExecutionRepository, PipelineRepository, WorkspaceRepository } from '@cat-factory/kernel';
11
+ import type { Clock, IdGenerator } from '@cat-factory/kernel';
12
+ import type { AgentExecutor } from '@cat-factory/kernel';
13
+ import type { WorkRunner } from '@cat-factory/kernel';
14
+ import type { ExecutionEventPublisher } from '@cat-factory/kernel';
15
+ import type { DocumentRepository } from '@cat-factory/kernel';
16
+ import type { TaskRepository } from '@cat-factory/kernel';
17
+ import type { RequirementReviewRepository } from '@cat-factory/kernel';
18
+ import type { ClarityReviewRepository } from '@cat-factory/kernel';
19
+ import type { EnvironmentProvisioningService } from '@cat-factory/integrations';
20
+ import type { BoardService } from '../board/BoardService.js';
21
+ import type { SpendService } from '@cat-factory/spend';
22
+ import type { AdvanceOptions, AdvanceResult } from './advance.js';
23
+ export interface ExecutionServiceDependencies {
24
+ workspaceRepository: WorkspaceRepository;
25
+ blockRepository: BlockRepository;
26
+ pipelineRepository: PipelineRepository;
27
+ executionRepository: ExecutionRepository;
28
+ /**
29
+ * Resolves the owning account of a workspace so a service that pins no cloud
30
+ * provider falls back to the account's `defaultCloudProvider` at dispatch.
31
+ */
32
+ accountRepository: AccountRepository;
33
+ idGenerator: IdGenerator;
34
+ clock: Clock;
35
+ agentExecutor: AgentExecutor;
36
+ workRunner: WorkRunner;
37
+ executionEventPublisher: ExecutionEventPublisher;
38
+ boardService: BoardService;
39
+ spendService: SpendService;
40
+ /**
41
+ * Optional: when the document-source integration is configured, documents
42
+ * linked to a block are resolved here and fed to the agent as extra context.
43
+ */
44
+ documentRepository?: DocumentRepository;
45
+ /**
46
+ * Optional: when the task-source integration is configured, tracker issues
47
+ * linked to a block are resolved here and fed to the agent as extra context.
48
+ */
49
+ taskRepository?: TaskRepository;
50
+ /**
51
+ * Optional: when the requirements-review feature is configured, a block's
52
+ * reworked ("incorporated") requirements are read here. When present they REPLACE
53
+ * the block's description + linked docs/tasks as the agent context (for every
54
+ * step) and become the per-task input the spec-writer aggregates. Absent
55
+ * → the engine uses the original description + docs/tasks unchanged.
56
+ */
57
+ requirementReviewRepository?: RequirementReviewRepository;
58
+ /**
59
+ * Optional: the requirements-review feature's service, present when the reviewer is
60
+ * wired. Drives the special `requirements-review` gate step (run reviewer inline, the
61
+ * iterative answer → incorporate → re-review loop). Absent → the gate step passes
62
+ * through so pipelines run unchanged without the feature.
63
+ */
64
+ requirementReviewService?: RequirementReviewService;
65
+ /**
66
+ * Optional: persistence for the clarity-review (bug-report triage) feature. Read here
67
+ * to substitute a converged clarified report as the downstream agent context (the
68
+ * mirror of `requirementReviewRepository`). Absent → no substitution.
69
+ */
70
+ clarityReviewRepository?: ClarityReviewRepository;
71
+ /**
72
+ * Optional: the clarity-review feature's service, present when the reviewer is wired.
73
+ * Drives the special `clarity-review` gate step (inline reviewer + the iterative
74
+ * answer → incorporate → re-review loop). Absent → the gate step passes through.
75
+ */
76
+ clarityReviewService?: ClarityReviewService;
77
+ /**
78
+ * Optional: when the individual-usage subscription store is configured, a finished
79
+ * run's per-run credential activation is deleted here the moment it reaches a terminal
80
+ * state, bounding standing exposure to the run's own lifetime (the TTL sweep is the
81
+ * backstop). Absent → activations are reclaimed by the TTL sweep alone.
82
+ */
83
+ subscriptionActivationRepository?: SubscriptionActivationRepository;
84
+ /**
85
+ * Optional: resolve a workspace's per-agent-kind default model id (the same resolver
86
+ * the container executor uses for dispatch). The personal-credential gate consults it
87
+ * so a run whose block has NO pinned model but whose workspace default resolves to an
88
+ * individual-usage vendor is still gated up-front — matching what dispatch will resolve,
89
+ * instead of starting and then failing on a missing activation. Absent → the gate sees
90
+ * only the block's pinned model (env-routing defaults are operator-level and not gated).
91
+ */
92
+ resolveWorkspaceModelDefault?: (workspaceId: string, agentKind: string) => Promise<string | undefined>;
93
+ /**
94
+ * Optional: resolve the provider capabilities (configured direct keys +
95
+ * subscription vendors + whether Cloudflare AI is enabled) for a workspace and the
96
+ * run initiator. The start guard uses it to block a pipeline whose steps' canonical
97
+ * models have no usable provider. Absent → the guard is skipped (tests / unconfigured
98
+ * facades), exactly like the existing optional engine deps.
99
+ */
100
+ resolveProviderCapabilities?: (workspaceId: string, initiatedBy?: string | null) => Promise<ProviderCapabilities>;
101
+ /**
102
+ * Optional: when the environment integration is configured, a `deployer` step
103
+ * provisions an ephemeral environment deterministically through this service
104
+ * (no LLM), and downstream steps discover the resulting env via it.
105
+ */
106
+ environmentProvisioning?: EnvironmentProvisioningService;
107
+ /**
108
+ * Optional: when the board-scan module is configured, a `blueprints` step's
109
+ * decomposition tree is reconciled onto the board through this (BoardScanService).
110
+ * Absent → a blueprint step still runs and commits its in-repo files, but the
111
+ * board isn't auto-updated from it.
112
+ */
113
+ blueprintReconciler?: BlueprintReconciler;
114
+ /**
115
+ * Optional: raises human-actionable notifications (a PR needs a merge decision,
116
+ * a no-merger pipeline finished, CI fixing gave up). Absent → those events still
117
+ * transition the block but no notification surfaces (tests).
118
+ */
119
+ notificationService?: NotificationService;
120
+ /**
121
+ * Optional: resolves a workspace's runtime settings so {@link ExecutionService.start}
122
+ * can enforce the per-service running-task limit. Absent → the limit is never enforced
123
+ * (tests / unconfigured facades start runs unbounded).
124
+ */
125
+ workspaceSettingsService?: WorkspaceSettingsService;
126
+ /**
127
+ * Optional: reads a block's CI check runs so the `ci` step can gate the PR on
128
+ * green CI. Absent → the `ci` step is a pass-through (nothing to gate), so the
129
+ * engine works unchanged when GitHub CI isn't wired.
130
+ */
131
+ ciStatusProvider?: CiStatusProvider;
132
+ /**
133
+ * Optional: reads a block's PR mergeability so the `conflicts` step can gate the
134
+ * PR on being mergeable. Absent → the `conflicts` step is a pass-through (nothing
135
+ * to gate), so the engine works unchanged when GitHub isn't wired.
136
+ */
137
+ mergeabilityProvider?: PullRequestMergeabilityProvider;
138
+ /**
139
+ * Optional: reads a deployed release's Datadog monitors/SLOs so the
140
+ * `post-release-health` step can gate on the release looking healthy after deploy.
141
+ * Absent → the step is a pass-through (nothing to watch), so the engine works
142
+ * unchanged when Datadog isn't wired.
143
+ */
144
+ releaseHealthProvider?: ReleaseHealthProvider;
145
+ /**
146
+ * Optional: annotates an incident PagerDuty / incident.io already opened from the
147
+ * same monitors/SLOs with the on-call agent's investigation. Best-effort enrichment,
148
+ * NOT alerting (those systems already paged). Absent → no enrichment.
149
+ */
150
+ incidentEnrichment?: IncidentEnrichmentProvider;
151
+ /**
152
+ * Optional: performs the real GitHub merge when a task should become `done`.
153
+ * Absent → `done` is a board-only flip (tests); when wired, `done` provably
154
+ * means the PR was merged on the remote.
155
+ */
156
+ pullRequestMerger?: PullRequestMerger;
157
+ /**
158
+ * Optional: resolves a task's merge threshold preset (auto-merge ceilings + the
159
+ * CI-fixer attempt budget). Absent → the built-in {@link DEFAULT_MERGE_PRESET}.
160
+ */
161
+ mergePresetRepository?: MergePresetRepository;
162
+ /**
163
+ * Optional: files a GitHub issue / Jira ticket for the `tracker` step (the
164
+ * tech-debt recurring pipeline). Absent → the `tracker` step passes through
165
+ * without filing anything, so the engine works unchanged when no tracker is wired.
166
+ */
167
+ ticketTrackerProvider?: TicketTrackerProvider;
168
+ /**
169
+ * Optional: the LLM observability sink. When wired, each emit rolls the per-run
170
+ * model-call aggregates onto the matching pipeline steps (`step.metrics`) so the
171
+ * board shows tokens / output-limit headroom / transport-vs-execution latency
172
+ * live. Absent (tests / unconfigured) → steps carry no `metrics`.
173
+ */
174
+ llmObservability?: LlmObservabilityService;
175
+ /**
176
+ * Optional: whether the runtime can run the Tester's LOCAL docker-compose infra via
177
+ * Docker-in-Docker. Defaults to `true` (Cloudflare, Node, tests). The local facade
178
+ * sets it `false` for runtimes without nesting (Apple `container`), which makes
179
+ * {@link ExecutionService.assertTesterInfraConfigured} refuse a local-infra Tester run
180
+ * (steering it to the ephemeral environment or a no-infra service) instead of
181
+ * dispatching a job that can't stand its dependencies up.
182
+ */
183
+ localTestInfraSupported?: boolean;
184
+ }
185
+ /** Reconciles a Blueprinter step's tree onto the board in place (BoardScanService). */
186
+ export interface BlueprintReconciler {
187
+ reconcileBlueprint(workspaceId: string, frameId: string | null, service: BlueprintService): Promise<unknown>;
188
+ }
189
+ /**
190
+ * The execution engine. It orchestrates a pipeline of agent-performed steps and
191
+ * is fully deterministic: `advanceInstance` moves one run forward by exactly one
192
+ * step, delegating the actual work — and the choice of whether to pause for a
193
+ * human decision — to the injected {@link AgentExecutor}. The durable workflow
194
+ * driver calls it in a loop. All LLM behaviour lives behind that port, so the
195
+ * engine here can be tested with a
196
+ * deterministic fake and no timing/delays.
197
+ */
198
+ export declare class ExecutionService {
199
+ private readonly workspaceRepository;
200
+ private readonly blockRepository;
201
+ private readonly pipelineRepository;
202
+ private readonly executionRepository;
203
+ private readonly accountRepository;
204
+ private readonly idGenerator;
205
+ private readonly clock;
206
+ private readonly agentExecutor;
207
+ private readonly workRunner;
208
+ private readonly events;
209
+ private readonly board;
210
+ private readonly spend;
211
+ private readonly requirementReviewService?;
212
+ private readonly clarityReviewService?;
213
+ private readonly environmentProvisioning?;
214
+ /** Assembles the per-step agent context (requirements, docs, env, service frame, fragments). */
215
+ private readonly contextBuilder;
216
+ /** Resolves a `merger` step's assessment into an auto-merge or a `merge_review` notification. */
217
+ private readonly mergeResolver;
218
+ /** Drives a companion (reviewer/spec/architect) step: grade → pass / loop producer / park. */
219
+ private readonly companionController;
220
+ /** Drives the Tester gate's fix loop: report → greenlight / dispatch fixer / fail. */
221
+ private readonly testerController;
222
+ /** Drives both iterative review gates (requirements + clarity); kind-parameterised. */
223
+ private readonly reviewGate;
224
+ /** The requirements subject for {@link reviewGate}. */
225
+ private readonly requirementsKind;
226
+ /** The clarity (bug-report triage) subject for {@link reviewGate}. */
227
+ private readonly clarityKind;
228
+ private readonly blueprintReconciler?;
229
+ private readonly notificationService?;
230
+ private readonly workspaceSettingsService?;
231
+ private readonly llmObservability?;
232
+ private readonly ciStatusProvider?;
233
+ private readonly mergeabilityProvider?;
234
+ private readonly releaseHealthProvider?;
235
+ private readonly incidentEnrichment?;
236
+ private readonly prMerger?;
237
+ private readonly mergePresetRepository?;
238
+ private readonly ticketTrackerProvider?;
239
+ private readonly subscriptionActivations?;
240
+ private readonly resolveProviderCapabilities?;
241
+ private readonly resolveWorkspaceModelDefault?;
242
+ /** Whether the runtime can run the Tester's local DinD infra (false = limited mode). */
243
+ private readonly localTestInfraSupported;
244
+ /** Lazily-built polling-gate registry, keyed by `agentKind`. See {@link gateFor}. */
245
+ private gateRegistryCache?;
246
+ /**
247
+ * Lazily-built post-completion resolver registry, keyed by `agentKind`. See
248
+ * {@link stepResolverFor} and {@link StepCompletionResolver}.
249
+ */
250
+ private stepResolverCache?;
251
+ constructor({ workspaceRepository, blockRepository, pipelineRepository, executionRepository, accountRepository, idGenerator, clock, agentExecutor, workRunner, executionEventPublisher, boardService, spendService, documentRepository, taskRepository, requirementReviewRepository, requirementReviewService, clarityReviewRepository, clarityReviewService, environmentProvisioning, blueprintReconciler, notificationService, workspaceSettingsService, llmObservability, ciStatusProvider, mergeabilityProvider, releaseHealthProvider, incidentEnrichment, pullRequestMerger, mergePresetRepository, ticketTrackerProvider, subscriptionActivationRepository, resolveWorkspaceModelDefault, resolveProviderCapabilities, localTestInfraSupported, }: ExecutionServiceDependencies);
252
+ private requireWorkspace;
253
+ private requireBlock;
254
+ /**
255
+ * The individual-usage subscription vendors a run STARTED against `blockId` with
256
+ * `pipelineId` will lease a personal credential for — so the controller can gate the
257
+ * run on the initiator's personal subscription(s) up-front. Mirrors the dispatch-time
258
+ * model precedence (block pin → workspace per-kind default) across every step, AND the
259
+ * per-user dispatch decision: `hasPersonalSubscription(vendor)` reports whether the
260
+ * initiator has their own subscription for a vendor, so a dual-mode model (GLM) only
261
+ * gates a subscriber (a non-subscriber runs it on the Cloudflare base, ungated).
262
+ * Defaults to "no personal subscription" for system/unauthenticated callers.
263
+ */
264
+ individualVendorsForBlock(workspaceId: string, blockId: string, pipelineId: string, hasPersonalSubscription?: HasPersonalSubscription): Promise<SubscriptionVendor[]>;
265
+ /** The individual-usage vendors a failed run's resumed steps use (for the retry gate). */
266
+ individualVendorsForRun(workspaceId: string, executionId: string, hasPersonalSubscription?: HasPersonalSubscription): Promise<SubscriptionVendor[]>;
267
+ /**
268
+ * The set of individual-usage vendors the given steps resolve to, used to gate a run
269
+ * on the initiator's personal subscription(s) up-front. Delegates to the pure
270
+ * {@link resolveIndividualVendors}, which mirrors the dispatch-time precedence: a
271
+ * resolvable block pin decides the set alone (NONE for a non-subscription model), and
272
+ * only an unpinned run falls to the workspace per-kind defaults.
273
+ */
274
+ private resolveIndividualVendors;
275
+ /**
276
+ * Guard a Tester pipeline's start: local-mode testing must have its infra
277
+ * configured on the service frame — either a docker-compose path to stand the
278
+ * dependencies up, or the explicit "no infra dependencies" flag. Ephemeral-mode
279
+ * testing uses the provisioned environment, so it needs neither. Throws a
280
+ * {@link ConflictError} (surfaced as an actionable message) when neither is set.
281
+ */
282
+ private assertTesterInfraConfigured;
283
+ /**
284
+ * Guard a pipeline's start on having a usable provider for every step's canonical
285
+ * model. The model a step runs is resolved by the same precedence the dispatch path
286
+ * uses (block pin → workspace per-kind default); each canonical id must have a usable
287
+ * provider given what's configured — a direct API key for its provider, a connected
288
+ * subscription vendor, or the opt-in Cloudflare lib enabled. Env-routing defaults (the
289
+ * last fallback, with no catalog id) are operator-level and not gated, matching the
290
+ * personal-credential gate. A throw aborts the start cleanly before any side effects.
291
+ * Skipped when no capability resolver is wired (tests / unconfigured facades).
292
+ */
293
+ private assertProvidersConfiguredForPipeline;
294
+ /** Start a pipeline against a block, replacing any prior run on it. */
295
+ start(workspaceId: string, blockId: string, pipelineId: string,
296
+ /**
297
+ * Internal user id of the initiator. Recorded on the run so an individual-usage
298
+ * model (Claude) uses this user's OWN personal subscription. Absent for
299
+ * system-initiated runs (recurring schedules) and auth-disabled dev.
300
+ */
301
+ initiatedBy?: string | null,
302
+ /**
303
+ * Mint the per-run personal-credential activation for an individual-usage model.
304
+ * Invoked with the new run's id BEFORE it is persisted/dispatched, so the async
305
+ * steps can lease it; a throw (wrong/missing password) aborts the start cleanly
306
+ * with nothing persisted. The server layer supplies this (the personal store lives
307
+ * outside the domain Core); absent for non-individual runs.
308
+ */
309
+ activate?: (executionId: string) => Promise<void>): Promise<ExecutionInstance>;
310
+ /**
311
+ * Enforce the workspace's per-service running-task limit before a task run starts.
312
+ * No-ops unless the settings module is wired, the block is a task, and a limit mode
313
+ * is active. Counts the tasks under the same service frame that already have a live
314
+ * run (running / blocked / paused) — bucketed by task type when the mode is
315
+ * `per_type`, else shared across all types — and throws a {@link ConflictError} (→ 409,
316
+ * shown as a toast) when the cap is reached. The starting block is excluded from the
317
+ * count (its prior run is about to be replaced).
318
+ */
319
+ private assertWithinTaskLimit;
320
+ /**
321
+ * Advance a single run by exactly one step and report what happened. This is
322
+ * the durable driver's entry point: it reloads the run from storage (so it is
323
+ * safe under replay/retry), no-ops unless the run is actively running, and
324
+ * otherwise performs one agent step via the shared {@link stepInstance} logic.
325
+ */
326
+ advanceInstance(workspaceId: string, executionId: string, options?: AdvanceOptions): Promise<AdvanceResult>;
327
+ /** Advance a single running instance by one step, persisting the result. */
328
+ private stepInstance;
329
+ /**
330
+ * Preview the model a step will run (`provider:model`) ahead of the work, so the
331
+ * board can show it during the inline query / container cold-boot rather than only
332
+ * once the result or job handle lands. Best-effort: the executor may not implement
333
+ * a preview, and a resolution failure (e.g. an unwired container kind that fails at
334
+ * dispatch anyway) must never break the run — both yield undefined.
335
+ */
336
+ private previewStepModel;
337
+ /**
338
+ * Whether the current step will run on a flat-rate subscription (quota) model, so
339
+ * the spend gate can let it proceed even when the monetary budget is exhausted.
340
+ * Resolved through the executor (the authority on the "subscriptions always win"
341
+ * routing) off a full step context. Best-effort and side-effect-free: an executor
342
+ * without the capability, a missing block, or any resolution error all report false
343
+ * (the step is treated as budget-metered, the prior behaviour). Only consulted on
344
+ * the over-budget path, so the extra context build never touches the happy path.
345
+ */
346
+ private currentStepIsQuotaBased;
347
+ /**
348
+ * Poll the asynchronous job a parked step dispatched. Returns `awaiting_job`
349
+ * while it runs (the driver keeps polling), records the result and advances on
350
+ * success, or reports `job_failed` so the driver can fail the run. Reading run
351
+ * state from storage on every call keeps it safe under Workflows replay/retry:
352
+ * once a job's result is recorded the step's `jobId` is cleared, so a re-poll
353
+ * simply lets the driver advance the now-current step.
354
+ */
355
+ pollAgentJob(workspaceId: string, executionId: string): Promise<AdvanceResult>;
356
+ /**
357
+ * Re-run a polling gate step's precheck from the durable driver's `awaiting_gate`
358
+ * loop: which gate (ci / conflicts) is resolved from the current step's `agentKind`,
359
+ * and it returns the same outcomes as the initial evaluation (precheck passes →
360
+ * advance, still computing → keep polling, fails → dispatch a helper or give up).
361
+ * Safe under replay: reads run state fresh each call. A no-op unless the current
362
+ * step is a gate actively in its `checking` phase.
363
+ */
364
+ pollGate(workspaceId: string, executionId: string): Promise<AdvanceResult>;
365
+ /**
366
+ * Decide what happens when the durable driver's GATE poll budget (ciMaxPolls ×
367
+ * ciPollInterval) is spent while a gate is still `pending` — called by both runtime
368
+ * drivers (Cloudflare ExecutionWorkflow / Node `driveExecution`) instead of failing
369
+ * the run directly, so the per-gate policy lives in one place. Most gates `fail`
370
+ * (CI never went green / the PR never became mergeable). A time-windowed watch gate
371
+ * (post-release-health, `pollExhaustion: 'pass'`) instead PASSES: the watch window
372
+ * simply outlasted the poll budget with no regression observed, which is healthy — not
373
+ * a timeout. Returns the result the driver should act on (it never re-fails for a fail
374
+ * gate; it returns a `job_failed` the driver funnels through its single `failRun`).
375
+ */
376
+ resolveGatePollExhaustion(workspaceId: string, executionId: string): Promise<AdvanceResult>;
377
+ /**
378
+ * Transition a step into `working`, stamping its start time the first time it
379
+ * actually begins. Set-once so a Workflows replay (which re-runs `advance`)
380
+ * preserves the original start rather than resetting it on every replay. An
381
+ * explicit re-run clears `startedAt` first (see {@link requestStepChanges}) so
382
+ * the fresh attempt is timed from scratch.
383
+ */
384
+ private startStep;
385
+ /**
386
+ * Transition a step into `done`, stamping its finish time once. Set-once so the
387
+ * approval-gate flow (which re-asserts `done` after a human approves, long after
388
+ * the agent actually finished) keeps the agent's true completion time, and so a
389
+ * replay doesn't move it. With {@link startStep}'s `startedAt` this yields the
390
+ * step's execution duration. A step finished directly out of a parked approval
391
+ * stopped *working* when it parked, so its duration is billed to the pause instant
392
+ * ({@link pauseStepForInput}), not the (later) moment the human decided.
393
+ */
394
+ private finishStep;
395
+ /**
396
+ * Finish a gated step that was skipped (its estimate gate was not satisfied) and either
397
+ * complete the run or advance to the next step — the deterministic finish/advance tail
398
+ * of {@link recordStepResult}, minus all the agent-result handling (no LLM ran, so there
399
+ * is no usage / decision / PR / artifact / approval / resolver to process). The step is
400
+ * marked `skipped` with empty output so the UI renders "skipped (gated)".
401
+ */
402
+ private skipGatedStep;
403
+ /**
404
+ * Park a step on a human decision and freeze its duration clock. Records when the
405
+ * step stopped working (`pausedAt`) so elapsed time no longer accrues while it waits
406
+ * for input — the symmetric counterpart of the terminal freeze on `finishedAt`.
407
+ * Set-once (a Workflows replay re-parking keeps the original instant); cleared when
408
+ * the step resumes ({@link startStep}) or finishes ({@link finishStep}).
409
+ */
410
+ private pauseStepForInput;
411
+ /**
412
+ * Record a completed agent step's result and report what the driver should do
413
+ * next: meter token usage, park on a raised decision, or persist the output
414
+ * (and any opened PR) and either finish the run or advance to the next step.
415
+ * Shared by the inline path and the async-job poll path.
416
+ */
417
+ private recordStepResult;
418
+ /**
419
+ * Reset a step so the durable driver re-runs it from scratch: clear its live
420
+ * container job handle (so it dispatches FRESH work rather than re-attaching to a
421
+ * finished or evicted job), its timings, approval gate, live subtasks and last
422
+ * output, and drop it back to `pending`. Preserves the step's identity
423
+ * (`agentKind` / `requiresApproval`) and any companion budget/verdict history.
424
+ */
425
+ private resetStepForRerun;
426
+ /**
427
+ * Loop a producer step back for rework and re-run every step from it up to and
428
+ * including the companion at `companionIndex`: each one is reset (crucially clearing
429
+ * stale container job handles so an intermediate container step re-dispatches fresh
430
+ * work instead of re-attaching to its evicted job), the producer is handed the
431
+ * `rework` feedback + started, and the instance cursor is moved back to the producer.
432
+ * Shared by the automatic companion loop and the human "request changes" path.
433
+ */
434
+ private rerunProducerThrough;
435
+ /**
436
+ * The index of the nearest preceding step a companion grades (one of its target
437
+ * producer kinds), or -1 when none precedes it. The single producer-search used by the
438
+ * automatic companion loop, the human "request changes" redirect, and the iteration-cap
439
+ * extra-round resolution.
440
+ */
441
+ private companionProducerIndex;
442
+ /**
443
+ * Loop a companion's producer back for one more automatic rework cycle: charge one
444
+ * attempt against the budget, then re-run the producer (and any intermediate steps) up
445
+ * to and including the companion so it re-grades. Shared by the automatic
446
+ * below-threshold loop ({@link evaluateCompanion}) and the human-granted extra round
447
+ * ({@link resolveCompanionExceeded}), so both consume the budget identically.
448
+ */
449
+ private loopCompanionProducer;
450
+ /**
451
+ * Deterministically provision an ephemeral environment for a deployer step.
452
+ * Produces a human-readable summary as the step output and reports no token
453
+ * usage (it incurs no LLM cost). Errors are swallowed into the output unless
454
+ * the durable driver wants them surfaced for its per-step retry.
455
+ */
456
+ private runDeployer;
457
+ /**
458
+ * File a tracking issue/ticket for a `tracker` step from the preceding `analysis`
459
+ * output. Non-LLM and best-effort: when no provider is wired or none is configured
460
+ * for the workspace it simply notes the skip; a filing error is folded into the
461
+ * step output rather than failing the run (the implementation still proceeds).
462
+ */
463
+ private runTracker;
464
+ /**
465
+ * The polling-gate registry, keyed by `agentKind`. A gate runs a programmatic
466
+ * precheck against a provider and only escalates to a helper container agent on a
467
+ * negative verdict. Built lazily (the closures capture `this`, so the providers /
468
+ * merge preset / notification helpers resolve at call time). Returns undefined for a
469
+ * non-gate kind. See {@link GateDefinition} and {@link evaluateGate}.
470
+ */
471
+ private gateFor;
472
+ /**
473
+ * The post-completion resolver for an agent kind, or undefined when the kind has none.
474
+ * A resolver runs DETERMINISTIC backend follow-up once the step's agent finishes — e.g.
475
+ * the merger performs the real GitHub merge — independent of the step's position in the
476
+ * pipeline. Built lazily (closures capture `this`). See {@link StepCompletionResolver}.
477
+ */
478
+ private stepResolverFor;
479
+ private buildStepResolverRegistry;
480
+ private buildGateRegistry;
481
+ /**
482
+ * Evaluate a polling gate step once and decide (shared by the initial advance and the
483
+ * durable `awaiting_gate` re-poll):
484
+ * - no provider wired → pass-through (advance; nothing to gate);
485
+ * - precheck passes → advance to the next step (the helper agent is NEVER spun up);
486
+ * - still computing → `awaiting_gate` (the driver sleeps then calls {@link pollGate});
487
+ * - fails, budget left → dispatch the helper container agent (`awaiting_job`);
488
+ * - fails, budget spent → the gate's exhaustion handler, then fail the run.
489
+ */
490
+ private evaluateGate;
491
+ /**
492
+ * Dispatch a gate's helper container agent on a failed precheck: build the agent
493
+ * context with the kind overridden to the helper (it clones the PR head branch and
494
+ * pushes — no new PR), park on the job, and flip the gate to `working`. Idempotent
495
+ * under replay via the step's `jobId` (re-attach handled in {@link evaluateGate}).
496
+ */
497
+ private dispatchGateHelper;
498
+ /**
499
+ * Raise a `decision_required` notification when a run parks on an iteration-cap gate
500
+ * after spending its automatic budget — a quality companion at its rework cap or an
501
+ * iterative reviewer (requirements / clarity) at its iteration cap. Without it the
502
+ * three-choice decision is reachable only by drilling into the parked step, so the run
503
+ * looks silently stuck. Best-effort: a missing notification service (tests) or block is
504
+ * a no-op.
505
+ */
506
+ private raiseDecisionRequired;
507
+ /**
508
+ * Ensure an open notification exists for a run that has just parked waiting for a human
509
+ * (an agent-raised decision, an approval gate, or an iterative review gate). Without
510
+ * the old decision timeout the run waits indefinitely, so the inbox card — which the
511
+ * periodic sweep escalates yellow → red — is the only signal a human is needed.
512
+ *
513
+ * Non-clobbering: if ANY open notification is already on the block (a more specific
514
+ * `merge_review`, iteration-cap `decision_required`, etc.), it is left untouched and we
515
+ * raise nothing — so the richer message wins. Best-effort: no notification service
516
+ * (tests) or a missing block is a no-op.
517
+ */
518
+ private ensureWaitingNotification;
519
+ /**
520
+ * Clear the auto-raised "waiting for a human decision" card once a run advances past
521
+ * the decision it was parked on (so the escalation sweep can't flip a settled decision
522
+ * red). Scoped to the `decision_required` type, so the human-actionable cards a stopped
523
+ * run leaves behind are untouched. Best-effort: no notification service (tests) is a no-op.
524
+ */
525
+ private clearWaitingNotification;
526
+ /** Raise a `ci_failed` notification when the CI gate exhausts its fixer budget. */
527
+ private raiseCiFailed;
528
+ /** Provision inputs (`{{input.*}}`) derived from the block under deployment. */
529
+ private deployInputs;
530
+ /**
531
+ * Typed git/PR/repo context for the deployer, derived from the block's PR ref. A
532
+ * PR-environment provider (e.g. an in-house adapter) needs the branch/repo to target
533
+ * the right environment; the same values are also flattened into `{{input.*}}` for
534
+ * the manifest path. `owner`/`repo` are parsed from the PR url when present.
535
+ */
536
+ private deployContext;
537
+ /**
538
+ * Invoke the agent for an already-built context. Failures are swallowed into the
539
+ * step output so a run never wedges — unless `rethrowAgentErrors` is set (the
540
+ * durable path), in which case the error propagates so the driver's per-step
541
+ * retry can take over.
542
+ */
543
+ private runAgent;
544
+ /**
545
+ * Strictly parse a Blueprinter step's tree and reconcile it onto the board. The
546
+ * blueprint maps the whole repository, so it is reconciled onto the run block's
547
+ * **service frame** (walked up from the block), not the task the run targeted.
548
+ * Best-effort and reconciler-gated: a parse/reconcile failure is logged-by-throw
549
+ * upstream only when the reconciler is wired; with no reconciler it is a no-op so
550
+ * the blueprint's in-repo files still land.
551
+ */
552
+ private ingestBlueprint;
553
+ /**
554
+ * Strictly validate a spec-writer step's unified specification. The canonical record
555
+ * is the in-repo `spec/` files the harness already committed; this is the trust
556
+ * boundary (a malformed payload is dropped, never trusted) plus a client refresh
557
+ * nudge. A persisted board projection is a deliberate later phase.
558
+ */
559
+ private ingestSpec;
560
+ /**
561
+ * Park a step on the durable decision-wait the approval gate uses, so a human (or the
562
+ * dedicated review window) can drive an iterative loop and resume the run. Shared by the
563
+ * requirements gate and the companion iteration-cap gate: both reuse the SAME parking
564
+ * mechanism rather than each rolling its own. `proposal` seeds the gate's stored text
565
+ * (the companion's latest feedback; empty for the requirements window, which renders its
566
+ * own structured surface via the universal result-view registry).
567
+ */
568
+ private parkStepOnDecision;
569
+ /**
570
+ * Two gates park on a `step.approval` but are NOT generic prose approvals — they are
571
+ * iterative gates driven by their own dedicated surface, never the generic
572
+ * approve/request-changes/reject resolvers (which would advance the run bypassing the
573
+ * loop). Guard those resolvers so a stray approve can't short-circuit either gate:
574
+ * - the requirements-review gate (driven by re-review / proceed / resolve-exceeded);
575
+ * - a companion gate that hit its rework cap (`companion.exceeded`), driven by
576
+ * {@link resolveCompanionExceeded}'s one-more-round / proceed / stop-reset choices.
577
+ */
578
+ private assertNotIterativeGate;
579
+ /**
580
+ * The requirements subject for {@link reviewGate}: closures over the requirements reviewer
581
+ * service. The service-not-configured guard preserves the exact 409 the inline reviewer
582
+ * raised before this extraction.
583
+ */
584
+ private buildRequirementsKind;
585
+ /**
586
+ * The clarity (bug-report triage) subject for {@link reviewGate}: threads any upstream
587
+ * `bug-investigator` output into the reviewer/incorporation context, otherwise identical to
588
+ * the requirements kind.
589
+ */
590
+ private buildClarityKind;
591
+ /**
592
+ * Run a fresh reviewer pass over a block's collected requirements, snapshotting the
593
+ * task's merge-preset knobs (iteration budget + tolerated severity) onto the review.
594
+ * Shared by the pipeline gate and the off-path inspector "Run review" surface, so both
595
+ * honour the task's preset identically.
596
+ */
597
+ reviewRequirements(workspaceId: string, blockId: string): Promise<RequirementReview>;
598
+ /**
599
+ * Incorporate the human's settled answers ASYNCHRONOUSLY. Validates that every finding is
600
+ * answered/dismissed, flags the review `incorporating`, records the intent on the parked
601
+ * gate step, and signals the durable driver to wake — which folds the answers and
602
+ * re-reviews in the background. Off-path (no parked run) the fold + re-review run inline.
603
+ */
604
+ incorporateRequirements(workspaceId: string, blockId: string, feedback?: string): Promise<RequirementReview>;
605
+ /**
606
+ * Re-review the incorporated document (one more reviewer pass). On convergence
607
+ * (`incorporated`) the parked run advances; otherwise the window shows the next cycle
608
+ * (`ready`) or the iteration-cap choices (`exceeded`).
609
+ */
610
+ reReviewRequirements(workspaceId: string, blockId: string): Promise<RequirementReview>;
611
+ /**
612
+ * Proceed: settle the requirements (the last incorporated doc, if any, becomes what
613
+ * downstream agents consume) and advance the parked run.
614
+ */
615
+ proceedRequirements(workspaceId: string, blockId: string): Promise<RequirementReview>;
616
+ /**
617
+ * Route an iteration-cap resolution to its gate-specific handlers. `stop-reset` is
618
+ * uniform across gates: cancel the run and return the block to phase zero (editable),
619
+ * keeping whatever reference artifact each gate persists (the requirements doc on its
620
+ * own table; a companion's producer output on its branch). Shared by the requirements
621
+ * gate ({@link resolveRequirementsExceeded}) and the companion gate
622
+ * ({@link resolveCompanionExceeded}) so the three-way choice lives in one place.
623
+ */
624
+ private dispatchIterationCap;
625
+ /**
626
+ * Resolve a requirements review that hit its iteration cap: grant one more round,
627
+ * proceed with the last incorporated doc, or stop the task and reset it to phase zero.
628
+ */
629
+ resolveRequirementsExceeded(workspaceId: string, blockId: string, choice: ResolveRequirementsExceededChoice): Promise<RequirementReview>;
630
+ /**
631
+ * Resolve a companion step parked at its automatic-rework cap (`companion.exceeded`):
632
+ * grant one more round, proceed accepting the producer's current output, or stop the
633
+ * task and reset it to phase zero. The companion mirror of
634
+ * {@link resolveRequirementsExceeded}, sharing the iteration-cap dispatch + the
635
+ * gate-resume plumbing. Idempotent — an already-resolved gate returns the instance
636
+ * unchanged. Scoped by execution + approval id (the execution controller surface),
637
+ * since a companion gate is not block-addressed like the requirements window.
638
+ */
639
+ resolveCompanionExceeded(workspaceId: string, executionId: string, approvalId: string, choice: IterationCapChoice): Promise<ExecutionInstance>;
640
+ /**
641
+ * Finish a gate step the human just resolved (its `approval` already marked `approved`),
642
+ * then either finish the run (final step) or advance to the next step, persist, and wake
643
+ * the parked durable driver. The single advance/finalize/signal path shared by every
644
+ * gate-resume site — the generic approval ({@link approveStep}), the review gates (via
645
+ * {@link ReviewGateController}) and the companion iteration-cap proceed
646
+ * ({@link resolveCompanionExceeded}) — so the logic lives in exactly one place.
647
+ */
648
+ private advancePastResolvedGate;
649
+ /** The latest `bug-investigator` step output on a run (the triage subject), or undefined. */
650
+ private investigationFor;
651
+ /** Resolve a block's investigator output via its current execution (off the gate path). */
652
+ private investigationForBlock;
653
+ /**
654
+ * Run a fresh clarity reviewer pass over a block's bug report, snapshotting the task's
655
+ * merge-preset knobs (iteration budget + tolerated severity) and threading in any
656
+ * `bug-investigator` output as the triage subject. Shared by the gate + the off-path
657
+ * inspector "Run review" surface.
658
+ */
659
+ reviewClarity(workspaceId: string, blockId: string): Promise<ClarityReview>;
660
+ /** Incorporate the human's settled answers ASYNCHRONOUSLY (the clarity mirror of {@link incorporateRequirements}). */
661
+ incorporateClarity(workspaceId: string, blockId: string, feedback?: string): Promise<ClarityReview>;
662
+ /** Re-review the clarified report (one more pass). On convergence the parked run advances. */
663
+ reReviewClarity(workspaceId: string, blockId: string): Promise<ClarityReview>;
664
+ /** Proceed: settle the clarity review and advance the parked run. */
665
+ proceedClarity(workspaceId: string, blockId: string): Promise<ClarityReview>;
666
+ /** Resolve a clarity review that hit its iteration cap (extra-round / proceed / stop-reset). */
667
+ resolveClarityExceeded(workspaceId: string, blockId: string, choice: ResolveRequirementsExceededChoice): Promise<ClarityReview>;
668
+ /**
669
+ * Push the run's latest state to subscribed clients, alongside its rolled-up
670
+ * block so the board updates without a refetch. Best-effort: the publisher
671
+ * swallows its own errors, and the persisted run remains the source of truth.
672
+ */
673
+ private emitInstance;
674
+ /**
675
+ * Roll the run's recorded LLM calls into per-step `metrics` for the board, in
676
+ * place on the emitted instance. The proxy keys calls by execution + agentKind
677
+ * (not step index), so the aggregate is per-agent-kind within the run; steps
678
+ * sharing a kind get the same rollup. Best-effort and a no-op when the sink is
679
+ * not wired, so it never blocks an emit.
680
+ */
681
+ private attachStepMetrics;
682
+ /** Set the block's in-progress/blocked status and step-completion progress. */
683
+ private updateBlockProgress;
684
+ /**
685
+ * Advance the block's step PROGRESS without touching its status — used when a step
686
+ * resolver already owns the block's terminal status (the merger set `done`/`pr_ready`)
687
+ * and a trailing step still follows, so the bar moves on without downgrading that status.
688
+ */
689
+ private refreshBlockProgress;
690
+ /**
691
+ * A pipeline finished. A frame becomes `done` (a mapping-only run leaves it
692
+ * `ready`). A *task* never auto-`done`s from a confidence score any more — that
693
+ * looked merged when the PR was still open with red CI. Instead:
694
+ * - if the pipeline has a `merger` step, it already owned the merge/notify
695
+ * decision (see {@link resolveMergerStep}); we only backstop a missing one;
696
+ * - otherwise the work is complete but unmerged: leave the PR open (`pr_ready`)
697
+ * and raise a `pipeline_complete` notification for a human to confirm + merge.
698
+ * `done` now strictly means the PR was merged (see {@link finalizeMerge}).
699
+ */
700
+ private finalizeBlock;
701
+ /**
702
+ * Merge a block's PR for real, then mark it `done`. The remote merge happens
703
+ * FIRST (via the {@link PullRequestMerger} port) and only on its success does the
704
+ * block flip to `done` — so `done` provably means "merged", not a board-only
705
+ * status. When no merger is wired (tests) this degrades to the old board-only
706
+ * flip. Throws if the remote merge fails so callers can fall back to a manual
707
+ * merge / review notification.
708
+ */
709
+ private finalizeMerge;
710
+ /**
711
+ * Resolve the merge threshold preset that governs a task: its explicitly-picked
712
+ * preset, else the workspace default, else the built-in {@link DEFAULT_MERGE_PRESET}.
713
+ * Returns just the thresholds the engine compares against (+ the CI attempt budget).
714
+ */
715
+ private resolveMergePreset;
716
+ /**
717
+ * Resolve a finished `on-call` investigation (the post-release-health gate's helper):
718
+ * parse its assessment, raise a `release_regression` notification for a human, enrich
719
+ * any incident PagerDuty/incident.io already opened, then finish the gate step so the
720
+ * run completes (the human acts on the notification out-of-band — the engine never
721
+ * auto-reverts). Best-effort on the side-effects; the step always finishes.
722
+ */
723
+ private resolveOnCallStep;
724
+ /** Raise a `release_regression` notification carrying the on-call assessment + signals. */
725
+ private raiseReleaseRegression;
726
+ /**
727
+ * Best-effort: annotate an incident PagerDuty / incident.io already opened (from the
728
+ * same monitors/SLOs) with the on-call investigation. NOT alerting — those systems
729
+ * already paged. A no-op when no provider is wired or no matching incident exists.
730
+ */
731
+ private enrichIncident;
732
+ /** Raise a `pipeline_complete` notification for a no-merger run awaiting confirmation. */
733
+ private raisePipelineComplete;
734
+ /**
735
+ * Implementing a task assigned to a module materialises that module: create it
736
+ * in the service if missing, then move the task inside it.
737
+ */
738
+ private applyModuleAssignment;
739
+ /** Resolve a pending decision; the run's next step lets the agent finish it. */
740
+ resolveDecision(workspaceId: string, executionId: string, decisionId: string, choice: string): Promise<ExecutionInstance>;
741
+ /**
742
+ * Approve a step's gated proposal: the run advances to the next step, carrying
743
+ * the (optionally human-edited) proposal forward as context. Mirrors
744
+ * {@link resolveDecision}'s durable-wake but *advances* the pipeline instead of
745
+ * re-running the step (the step is already done). Idempotent — re-approving an
746
+ * already-approved gate is a no-op.
747
+ */
748
+ approveStep(workspaceId: string, executionId: string, approvalId: string, opts?: {
749
+ proposal?: string;
750
+ }): Promise<ExecutionInstance>;
751
+ /**
752
+ * Request changes on a step's gated proposal: the same step re-runs with the
753
+ * human's freeform feedback and/or per-block comments (and its prior proposal)
754
+ * folded into the agent's context (see {@link AgentContextBuilder}). The run is left
755
+ * `running` on the same step; on the re-run's completion the gate is raised
756
+ * afresh. At least one of `feedback`/`comments` is expected (the controller
757
+ * validates this), but an empty review is harmless — the agent simply re-runs.
758
+ */
759
+ requestStepChanges(workspaceId: string, executionId: string, approvalId: string, review: {
760
+ feedback?: string;
761
+ comments?: StepReviewComment[];
762
+ }): Promise<ExecutionInstance>;
763
+ /**
764
+ * Reject a step's gated proposal: the run stops entirely. The gate is marked
765
+ * `rejected` and the run is failed with a dedicated `rejected` failure kind, so
766
+ * the board surfaces it via the shared failure banner (block → `blocked`) with a
767
+ * Retry affordance. The parked durable run is woken so it observes the now-terminal
768
+ * status and stops (the workflow's advance loop no-ops on a non-running run).
769
+ * Idempotent — rejecting an already-terminal gate is a no-op.
770
+ */
771
+ rejectStep(workspaceId: string, executionId: string, approvalId: string, reason?: string): Promise<ExecutionInstance>;
772
+ /** Merge an open PR: a block moves from `pr_ready` to `done`. */
773
+ mergePr(workspaceId: string, blockId: string): Promise<Block>;
774
+ /**
775
+ * Record a terminal agent failure: persist a structured {@link AgentFailure},
776
+ * flip the run to `failed`, and mark the block `blocked` (needs attention) — NOT
777
+ * `pr_ready`, which looked like success and hid the failure. The board then
778
+ * renders the same failure banner + retry as a failed bootstrap. Called by the
779
+ * durable driver once a step has exhausted its retries (or a job/decision
780
+ * faulted); `kind` classifies the cause so the right hint is shown.
781
+ */
782
+ failRun(workspaceId: string, executionId: string, message: string, kind?: AgentFailureKind, detail?: string | null): Promise<void>;
783
+ /**
784
+ * Retry a failed run: re-drive the same pipeline on the same block, **resuming
785
+ * from the step that actually failed** rather than restarting from step 0. The
786
+ * steps that already completed are preserved (so a `coder` failure in `pl_full`
787
+ * doesn't re-run the human-gated `requirements`/`architect` steps before it);
788
+ * the failed step and everything after it are reset to a clean, re-runnable
789
+ * state. Only a `failed` run can be retried.
790
+ *
791
+ * A fresh instance id is minted because the durable runner addresses one
792
+ * Workflows instance per execution id and the failed one is terminal — the new
793
+ * instance simply starts with `currentStep` pointed at the failed step, so the
794
+ * driver advances forward from there and never re-issues the completed steps'
795
+ * work. Mirrors {@link BootstrapService.retry}; both are reached via the unified
796
+ * `POST /agent-runs/:id/retry` endpoint.
797
+ */
798
+ retry(workspaceId: string, executionId: string,
799
+ /** The retrying user (their personal subscription is used for individual-usage
800
+ * models). Falls back to the original initiator when omitted. */
801
+ initiatedBy?: string | null,
802
+ /** Mint the per-run personal-credential activation (see {@link start}). */
803
+ activate?: (executionId: string) => Promise<void>): Promise<ExecutionInstance>;
804
+ /**
805
+ * Restart a run from a human-chosen step: re-run from `fromStepIndex` onward,
806
+ * regardless of how far the run had progressed (a `done`, `failed`, `blocked`,
807
+ * `paused` or still-`running` run are all valid sources). Unlike {@link retry}
808
+ * (which resumes at the first FAILURE) this rewinds to an arbitrary step the user
809
+ * picked — so it can re-run steps that already completed.
810
+ *
811
+ * What is preserved vs reset:
812
+ * - Steps BEFORE `fromStepIndex` keep their `output`/approval/timing untouched, so
813
+ * the engine still hands the restarted step its predecessors' work as
814
+ * `priorOutputs` (and their resolved `decisions`) — a useful handoff.
815
+ * - The chosen step and every later one are reset to a clean, re-runnable state,
816
+ * dropping each step's iteration counters (companion attempts, gate/test attempts,
817
+ * eviction recoveries) so the restart starts those loops from zero.
818
+ * - A block's incorporated requirements are NOT touched: they live on the
819
+ * requirement-review record, so a restarted spec-writer/coder still receives the
820
+ * incorporated document (or the base description when none was generated). When the
821
+ * chosen step is the `requirements-review` gate ITSELF, re-running it mints a fresh
822
+ * iteration-1 review (the reviewer's `review()` replaces the prior one), which is
823
+ * exactly the "reset the iterations counter from this step" semantics.
824
+ *
825
+ * Like {@link retry} a fresh instance id is minted (the durable runner addresses one
826
+ * driver per execution id). Any still-live driver/container for the run being
827
+ * replaced is torn down first, so restarting a RUNNING run never orphans a container
828
+ * or a parked Workflows instance.
829
+ */
830
+ restartFromStep(workspaceId: string, executionId: string, fromStepIndex: number,
831
+ /** The restarting user (their personal subscription is used for individual-usage
832
+ * models). Falls back to the original initiator when omitted. */
833
+ initiatedBy?: string | null,
834
+ /** Mint the per-run personal-credential activation (see {@link start}). */
835
+ activate?: (executionId: string) => Promise<void>): Promise<ExecutionInstance>;
836
+ /**
837
+ * Resume every run paused by the spend safeguard in this workspace. Flips them
838
+ * back to `running` and re-drives the durable runner. If the budget is still
839
+ * exhausted the spend gate will simply pause them again on their next step.
840
+ */
841
+ resumePaused(workspaceId: string): Promise<ExecutionInstance[]>;
842
+ /** Cancel the run on a block, returning it to `planned`. */
843
+ cancel(workspaceId: string, blockId: string): Promise<Block>;
844
+ /**
845
+ * Explicitly stop a *running* run by id (the unified `POST /agent-runs/:id/stop`
846
+ * surface): kill its per-run container, tear down the durable driver, then record
847
+ * a terminal `cancelled` failure so the board shows the run stopped (with retry)
848
+ * rather than spinning forever. Idempotent — a run already terminal is returned
849
+ * as-is. `opts.reason`/`opts.kind` let the orphan sweep reuse this with its own
850
+ * wording instead of the user-facing default.
851
+ */
852
+ stopRun(workspaceId: string, executionId: string, opts?: {
853
+ reason?: string;
854
+ kind?: AgentFailureKind;
855
+ }): Promise<ExecutionInstance>;
856
+ /**
857
+ * Tear down every run under a block subtree — kill each container, terminate each
858
+ * durable driver, and delete the run record — so deleting a service/module never
859
+ * orphans a container or a Workflows instance. Best-effort and silent: the board
860
+ * delete that follows emits the coarse refresh, so no per-run event is needed.
861
+ */
862
+ teardownForBlockTree(workspaceId: string, rootId: string): Promise<void>;
863
+ /**
864
+ * Best-effort: reclaim the per-run container backing an execution. The container is
865
+ * addressed by the run (execution) id, so a backend that shares one across the run
866
+ * (Cloudflare, local Docker) tears the whole thing down. A per-job backend (a
867
+ * self-hosted pool) has no run container, so it cancels the run's IN-FLIGHT step job
868
+ * instead — hence we pass the current step's job id alongside the run id. A no-op for
869
+ * inline executors (no `stopJob`) and for an already-gone container/job; never
870
+ * throws, so it can't derail the teardown that calls it.
871
+ */
872
+ private stopRunContainer;
873
+ }
874
+ //# sourceMappingURL=ExecutionService.d.ts.map