@cat-factory/orchestration 0.124.0 → 0.124.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.
- package/dist/container/module-registry.d.ts +55 -0
- package/dist/container/module-registry.d.ts.map +1 -0
- package/dist/container/module-registry.js +61 -0
- package/dist/container/module-registry.js.map +1 -0
- package/dist/container/modules.d.ts +194 -0
- package/dist/container/modules.d.ts.map +1 -0
- package/dist/container/modules.js +1060 -0
- package/dist/container/modules.js.map +1 -0
- package/dist/container.d.ts +44 -20
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +73 -1116
- package/dist/container.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +8 -8
package/dist/container.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createAppCaches } from '@cat-factory/caching';
|
|
2
|
-
import {
|
|
2
|
+
import { ModuleRegistry } from './container/module-registry.js';
|
|
3
|
+
import { createServicesModule, createGitHubModule, createDocumentsModule, createTasksModule, createEnvironmentsModule, createRunnersModule, createBootstrapModule, createTesterQualityReviewer, createDocInterviewService, createForkChatService, resolveBlockRunContext, createRequirementsModule, createBrainstormModule, createKaizenModule, createClarityModule, createNotificationsModule, createSlackModule, createRiskPoliciesModule, createSharedStacksModule, createPreflightModule, createSandboxModule, createWorkspaceSettingsModule, createReleaseHealthModule, createPackageRegistriesModule, createPreviewModule, createIncidentEnrichmentModule, createModelPresetsModule, createServiceFragmentDefaultsModule, createTrackerModule, createRecurringModule, } from './container/modules.js';
|
|
3
4
|
import { ServiceMountService } from './modules/services/ServiceMountService.js';
|
|
4
5
|
import { NoopWorkRunner } from '@cat-factory/kernel';
|
|
5
6
|
import { NoopEventPublisher } from '@cat-factory/kernel';
|
|
@@ -18,15 +19,13 @@ import { LlmObservabilityService } from './modules/observability/LlmObservabilit
|
|
|
18
19
|
import { AgentContextObservabilityService } from './modules/observability/AgentContextObservabilityService.js';
|
|
19
20
|
import { SearchQueryObservabilityService } from './modules/observability/SearchQueryObservabilityService.js';
|
|
20
21
|
import { PlatformObservabilityService } from './modules/observability/PlatformObservabilityService.js';
|
|
21
|
-
import { GitHubInstallationService, RepoProvisioningService, GitHubService, GitHubSyncService, WebhookService, DocumentConnectionService,
|
|
22
|
+
import { GitHubInstallationService, RepoProvisioningService, GitHubService, GitHubSyncService, WebhookService, DocumentConnectionService, DocumentImportService, DocumentPlannerService, DocumentLinkService, TaskConnectionService, TaskImportService, TaskLinkService, BugIntakeService, EnvironmentConnectionService, EnvironmentProvisioningService, EnvironmentTeardownService, EnvironmentUserHandlerService, RunnerPoolConnectionService, PreflightService, ProvisioningLogRecorder, ProvisioningLogService, SharedStackService, SlackConnectionService, SlackSettingsService, SlackMemberMappingService, } from '@cat-factory/integrations';
|
|
22
23
|
import { BootstrapService } from './modules/bootstrap/BootstrapService.js';
|
|
23
24
|
import { EnvConfigRepairService } from './modules/envConfigRepair/EnvConfigRepairService.js';
|
|
24
25
|
import { EnvironmentTestService } from './modules/environments/EnvironmentTestService.js';
|
|
25
26
|
import { BoardScanService } from './modules/boardScan/BoardScanService.js';
|
|
26
27
|
import { RequirementReviewService } from './modules/requirements/RequirementReviewService.js';
|
|
27
|
-
import {
|
|
28
|
-
import { ForkChatService } from './modules/execution/ForkChatService.js';
|
|
29
|
-
import { TesterQualityReviewService, } from './modules/execution/TesterQualityReviewService.js';
|
|
28
|
+
import {} from './modules/execution/TesterQualityReviewService.js';
|
|
30
29
|
import { KaizenService } from './modules/kaizen/KaizenService.js';
|
|
31
30
|
import { ClarityReviewService } from './modules/clarity/ClarityReviewService.js';
|
|
32
31
|
import { BrainstormService } from './modules/brainstorm/BrainstormService.js';
|
|
@@ -52,1023 +51,6 @@ import { defaultAgentKindRegistry, defaultInitiativePresetRegistry, FragmentLibr
|
|
|
52
51
|
import { createFragmentLibraryModule, createSkillLibraryModule, } from './container-content-libraries.js';
|
|
53
52
|
import { defaultGateRegistry, defaultStepResolverRegistry } from '@cat-factory/kernel';
|
|
54
53
|
/** Assemble the in-org service-sharing module when its repositories are wired. */
|
|
55
|
-
function createServicesModule(deps) {
|
|
56
|
-
const { serviceRepository, workspaceMountRepository } = deps;
|
|
57
|
-
if (!serviceRepository || !workspaceMountRepository)
|
|
58
|
-
return undefined;
|
|
59
|
-
const service = new ServiceMountService({
|
|
60
|
-
serviceRepository,
|
|
61
|
-
workspaceMountRepository,
|
|
62
|
-
workspaceRepository: deps.workspaceRepository,
|
|
63
|
-
idGenerator: deps.idGenerator,
|
|
64
|
-
clock: deps.clock,
|
|
65
|
-
});
|
|
66
|
-
return { service };
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* Assemble the GitHub module when every dependency it needs is present;
|
|
70
|
-
* otherwise return undefined so the feature stays cleanly opt-in.
|
|
71
|
-
*/
|
|
72
|
-
function createGitHubModule(deps, caches) {
|
|
73
|
-
const { githubClient, githubInstallationRepository, repoProjectionRepository, branchProjectionRepository, pullRequestProjectionRepository, issueProjectionRepository, commitProjectionRepository, checkRunProjectionRepository, webhookVerifier, } = deps;
|
|
74
|
-
if (!githubClient ||
|
|
75
|
-
!githubInstallationRepository ||
|
|
76
|
-
!repoProjectionRepository ||
|
|
77
|
-
!branchProjectionRepository ||
|
|
78
|
-
!pullRequestProjectionRepository ||
|
|
79
|
-
!issueProjectionRepository ||
|
|
80
|
-
!commitProjectionRepository ||
|
|
81
|
-
!checkRunProjectionRepository ||
|
|
82
|
-
!webhookVerifier) {
|
|
83
|
-
return undefined;
|
|
84
|
-
}
|
|
85
|
-
const installationService = new GitHubInstallationService({
|
|
86
|
-
githubClient,
|
|
87
|
-
githubInstallationRepository,
|
|
88
|
-
workspaceRepository: deps.workspaceRepository,
|
|
89
|
-
clock: deps.clock,
|
|
90
|
-
canCreateRepos: deps.canCreateRepos,
|
|
91
|
-
workflowsGranted: deps.workflowsGranted,
|
|
92
|
-
});
|
|
93
|
-
const syncService = new GitHubSyncService({
|
|
94
|
-
githubClient,
|
|
95
|
-
githubInstallationRepository,
|
|
96
|
-
repoProjectionRepository,
|
|
97
|
-
branchProjectionRepository,
|
|
98
|
-
pullRequestProjectionRepository,
|
|
99
|
-
issueProjectionRepository,
|
|
100
|
-
commitProjectionRepository,
|
|
101
|
-
checkRunProjectionRepository,
|
|
102
|
-
userRepoAccessRepository: deps.userRepoAccessRepository,
|
|
103
|
-
clock: deps.clock,
|
|
104
|
-
commitBackfillHorizonMs: deps.commitBackfillHorizonMs,
|
|
105
|
-
// Drop a workspace's cached repo projection (slice 3) after any link/sync write.
|
|
106
|
-
repoProjectionCache: caches.repoProjection,
|
|
107
|
-
// Serve the add-service picker's PAT typeahead from a per-user cache (filter in memory)
|
|
108
|
-
// instead of re-walking `/user/repos` on every keystroke.
|
|
109
|
-
viewerReposCache: caches.viewerRepos,
|
|
110
|
-
});
|
|
111
|
-
const webhookService = new WebhookService({
|
|
112
|
-
githubInstallationRepository,
|
|
113
|
-
repoProjectionRepository,
|
|
114
|
-
branchProjectionRepository,
|
|
115
|
-
pullRequestProjectionRepository,
|
|
116
|
-
issueProjectionRepository,
|
|
117
|
-
commitProjectionRepository,
|
|
118
|
-
checkRunProjectionRepository,
|
|
119
|
-
clock: deps.clock,
|
|
120
|
-
repoProjectionCache: caches.repoProjection,
|
|
121
|
-
// Drop a pushed branch's cached RepoFiles reads (slice 4) when a branch moves out-of-band.
|
|
122
|
-
repoFilesCache: caches.repoFiles,
|
|
123
|
-
// Repo-sourced skill freshness fan-out (slice 4): on a push, resync every skill source
|
|
124
|
-
// linked to the repo. Both are facade-provided (the queue-backed enqueue only exists where
|
|
125
|
-
// a runtime has a sync queue); unwired ⇒ the dispatch-time probe is the freshness backstop.
|
|
126
|
-
skillSourceRepository: deps.skillSourceRepository,
|
|
127
|
-
enqueueSkillResync: deps.enqueueSkillResync,
|
|
128
|
-
});
|
|
129
|
-
const service = new GitHubService({
|
|
130
|
-
githubClient,
|
|
131
|
-
repoProjectionRepository,
|
|
132
|
-
branchProjectionRepository,
|
|
133
|
-
pullRequestProjectionRepository,
|
|
134
|
-
issueProjectionRepository,
|
|
135
|
-
clock: deps.clock,
|
|
136
|
-
});
|
|
137
|
-
const provisioningService = deps.repoProvisioningClient
|
|
138
|
-
? new RepoProvisioningService({ client: deps.repoProvisioningClient })
|
|
139
|
-
: undefined;
|
|
140
|
-
return {
|
|
141
|
-
installationService,
|
|
142
|
-
syncService,
|
|
143
|
-
webhookService,
|
|
144
|
-
service,
|
|
145
|
-
webhookVerifier,
|
|
146
|
-
provisioningService,
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
/**
|
|
150
|
-
* Assemble the document-source module when at least one provider + both
|
|
151
|
-
* repositories are present. The model provider is optional: with it the planner
|
|
152
|
-
* uses an LLM, and without it the deterministic heading parser — so the module
|
|
153
|
-
* stays usable for import/link/spawn even when no LLM is configured.
|
|
154
|
-
*/
|
|
155
|
-
function createDocumentsModule(deps, boardService) {
|
|
156
|
-
const { documentSourceProviders, documentConnectionRepository, documentRepository } = deps;
|
|
157
|
-
if (!documentSourceProviders ||
|
|
158
|
-
documentSourceProviders.length === 0 ||
|
|
159
|
-
!documentConnectionRepository ||
|
|
160
|
-
!documentRepository) {
|
|
161
|
-
return undefined;
|
|
162
|
-
}
|
|
163
|
-
const registry = new MapDocumentSourceRegistry(documentSourceProviders);
|
|
164
|
-
const connectionService = new DocumentConnectionService({
|
|
165
|
-
documentConnectionRepository,
|
|
166
|
-
registry,
|
|
167
|
-
workspaceRepository: deps.workspaceRepository,
|
|
168
|
-
clock: deps.clock,
|
|
169
|
-
});
|
|
170
|
-
const importService = new DocumentImportService({
|
|
171
|
-
registry,
|
|
172
|
-
documentRepository,
|
|
173
|
-
connectionService,
|
|
174
|
-
workspaceRepository: deps.workspaceRepository,
|
|
175
|
-
clock: deps.clock,
|
|
176
|
-
});
|
|
177
|
-
const plannerService = new DocumentPlannerService({
|
|
178
|
-
modelProviderResolver: deps.modelProviderResolver,
|
|
179
|
-
modelProvider: deps.modelProvider,
|
|
180
|
-
modelRef: deps.documentPlannerModel,
|
|
181
|
-
});
|
|
182
|
-
const linkService = new DocumentLinkService({
|
|
183
|
-
boardService,
|
|
184
|
-
blockRepository: deps.blockRepository,
|
|
185
|
-
documentRepository,
|
|
186
|
-
});
|
|
187
|
-
const contentResolver = new DocumentContentResolverService({ registry, connectionService });
|
|
188
|
-
return { connectionService, importService, plannerService, linkService, contentResolver };
|
|
189
|
-
}
|
|
190
|
-
/**
|
|
191
|
-
* Assemble the task-source module when at least one provider + both repositories
|
|
192
|
-
* are present; otherwise return undefined so the feature stays cleanly opt-in.
|
|
193
|
-
* Unlike the documents module there is no planner — issues are linked for
|
|
194
|
-
* context, not expanded into board structure.
|
|
195
|
-
*/
|
|
196
|
-
function createTasksModule(deps, boardService) {
|
|
197
|
-
const { taskSourceProviders, taskConnectionRepository, taskSourceSettingsRepository, taskRepository, } = deps;
|
|
198
|
-
if (!taskSourceProviders ||
|
|
199
|
-
taskSourceProviders.length === 0 ||
|
|
200
|
-
!taskConnectionRepository ||
|
|
201
|
-
!taskSourceSettingsRepository ||
|
|
202
|
-
!taskRepository) {
|
|
203
|
-
return undefined;
|
|
204
|
-
}
|
|
205
|
-
const registry = new MapTaskSourceRegistry(taskSourceProviders);
|
|
206
|
-
const connectionService = new TaskConnectionService({
|
|
207
|
-
taskConnectionRepository,
|
|
208
|
-
taskSourceSettingsRepository,
|
|
209
|
-
registry,
|
|
210
|
-
workspaceRepository: deps.workspaceRepository,
|
|
211
|
-
clock: deps.clock,
|
|
212
|
-
// GitHub Issues' availability is the installed GitHub App's presence; absent when
|
|
213
|
-
// the GitHub integration isn't wired (the provider then isn't registered anyway).
|
|
214
|
-
...(deps.githubInstallationRepository
|
|
215
|
-
? { installations: deps.githubInstallationRepository }
|
|
216
|
-
: {}),
|
|
217
|
-
// Linear OAuth app credentials live in per-account deployment settings (sealed),
|
|
218
|
-
// resolved dynamically — mirroring the Slack OAuth model. Absent ⇒ the "Connect with
|
|
219
|
-
// Linear" flow isn't offered (manual API-key paste still works).
|
|
220
|
-
...(deps.accountSettings
|
|
221
|
-
? {
|
|
222
|
-
resolveLinearOAuth: (accountKey) => deps.accountSettings.resolve(accountKey).then((s) => s.linearOAuth),
|
|
223
|
-
}
|
|
224
|
-
: {}),
|
|
225
|
-
});
|
|
226
|
-
const importService = new TaskImportService({
|
|
227
|
-
registry,
|
|
228
|
-
taskRepository,
|
|
229
|
-
connectionService,
|
|
230
|
-
workspaceRepository: deps.workspaceRepository,
|
|
231
|
-
clock: deps.clock,
|
|
232
|
-
});
|
|
233
|
-
const linkService = new TaskLinkService({
|
|
234
|
-
boardService,
|
|
235
|
-
blockRepository: deps.blockRepository,
|
|
236
|
-
taskRepository,
|
|
237
|
-
importService,
|
|
238
|
-
});
|
|
239
|
-
// The recurring bug-intake step's read-and-claim helper — wired only when a schedule
|
|
240
|
-
// repository is present (an intake fire resolves the schedule's `issueIntake` config by
|
|
241
|
-
// block). Composes the just-built import/link services + the source registry, so it stays
|
|
242
|
-
// provider-neutral and runtime-symmetric.
|
|
243
|
-
const bugIntakeService = deps.pipelineScheduleRepository
|
|
244
|
-
? new BugIntakeService({
|
|
245
|
-
pipelineScheduleRepository: deps.pipelineScheduleRepository,
|
|
246
|
-
taskSourceRegistry: registry,
|
|
247
|
-
taskConnectionRepository,
|
|
248
|
-
importService,
|
|
249
|
-
linkService,
|
|
250
|
-
taskRepository,
|
|
251
|
-
})
|
|
252
|
-
: undefined;
|
|
253
|
-
return {
|
|
254
|
-
connectionService,
|
|
255
|
-
importService,
|
|
256
|
-
linkService,
|
|
257
|
-
...(bugIntakeService ? { bugIntakeService } : {}),
|
|
258
|
-
};
|
|
259
|
-
}
|
|
260
|
-
/**
|
|
261
|
-
* Assemble the environment integration when its provider, both repositories and
|
|
262
|
-
* the secret cipher are present; otherwise return undefined so the feature stays
|
|
263
|
-
* cleanly opt-in (the deterministic deployer and env discovery in the engine are
|
|
264
|
-
* gated on the provisioning service being wired).
|
|
265
|
-
*/
|
|
266
|
-
function createEnvironmentsModule(deps, provisioningLog, eventPublisher, sharedStackService, preflightService) {
|
|
267
|
-
const { environmentConnectionRepository, environmentRegistryRepository, secretCipher } = deps;
|
|
268
|
-
if (!environmentConnectionRepository || !environmentRegistryRepository || !secretCipher) {
|
|
269
|
-
return undefined;
|
|
270
|
-
}
|
|
271
|
-
// Durable async config repair is wired when both the dispatcher (the side-effecting
|
|
272
|
-
// container plumbing) and the kind-scoped job repository are present. The repair service
|
|
273
|
-
// and the connection service are mutually dependent: the connection service's
|
|
274
|
-
// `dispatchConfigRepair` seam STARTS a repair run (→ repairService), and the repair run's
|
|
275
|
-
// success path RE-VALIDATES via the connection service. We break the cycle by capturing
|
|
276
|
-
// `repairService` in a closure that is only invoked at request time (after assignment).
|
|
277
|
-
const canRepair = !!(deps.envConfigRepairer && deps.envConfigRepairJobRepository);
|
|
278
|
-
let repairService;
|
|
279
|
-
const connectionService = new EnvironmentConnectionService({
|
|
280
|
-
environmentConnectionRepository,
|
|
281
|
-
workspaceRepository: deps.workspaceRepository,
|
|
282
|
-
secretCipher,
|
|
283
|
-
clock: deps.clock,
|
|
284
|
-
environmentBackendRegistry: deps.environmentBackendRegistry ?? defaultEnvironmentBackendRegistry(),
|
|
285
|
-
...(deps.customManifestTypeRepository
|
|
286
|
-
? { customManifestTypeRepository: deps.customManifestTypeRepository }
|
|
287
|
-
: {}),
|
|
288
|
-
...(deps.customManifestTypeRegistry
|
|
289
|
-
? { customManifestTypeRegistry: deps.customManifestTypeRegistry }
|
|
290
|
-
: {}),
|
|
291
|
-
...(deps.environmentCustomTlsSupported !== undefined
|
|
292
|
-
? { customTlsSupported: deps.environmentCustomTlsSupported }
|
|
293
|
-
: {}),
|
|
294
|
-
...(deps.environmentProvider ? { environmentProvider: deps.environmentProvider } : {}),
|
|
295
|
-
...(deps.environmentUrlSafetyPolicy ? { urlPolicy: deps.environmentUrlSafetyPolicy } : {}),
|
|
296
|
-
...(deps.resolveRepoFilesForCoords
|
|
297
|
-
? { resolveRepoFilesForWorkspace: deps.resolveRepoFilesForCoords }
|
|
298
|
-
: {}),
|
|
299
|
-
...(deps.detectionConventions ? { detectionConventions: deps.detectionConventions } : {}),
|
|
300
|
-
...(canRepair
|
|
301
|
-
? {
|
|
302
|
-
dispatchConfigRepair: (input) => repairService
|
|
303
|
-
.start(input.workspaceId, {
|
|
304
|
-
owner: input.owner,
|
|
305
|
-
repo: input.repo,
|
|
306
|
-
gitRef: input.gitRef,
|
|
307
|
-
issues: input.issues,
|
|
308
|
-
...(input.inputs ? { inputs: input.inputs } : {}),
|
|
309
|
-
...(input.promptOverride ? { promptOverride: input.promptOverride } : {}),
|
|
310
|
-
...(input.manifestPath ? { manifestPath: input.manifestPath } : {}),
|
|
311
|
-
})
|
|
312
|
-
.then((job) => ({ jobId: job.id })),
|
|
313
|
-
}
|
|
314
|
-
: {}),
|
|
315
|
-
...(provisioningLog ? { provisioningLog } : {}),
|
|
316
|
-
});
|
|
317
|
-
if (canRepair) {
|
|
318
|
-
repairService = new EnvConfigRepairService({
|
|
319
|
-
envConfigRepairJobRepository: deps.envConfigRepairJobRepository,
|
|
320
|
-
workspaceRepository: deps.workspaceRepository,
|
|
321
|
-
idGenerator: deps.idGenerator,
|
|
322
|
-
clock: deps.clock,
|
|
323
|
-
repairer: deps.envConfigRepairer,
|
|
324
|
-
...(deps.envConfigRepairRunner ? { runner: deps.envConfigRepairRunner } : {}),
|
|
325
|
-
...(eventPublisher ? { eventPublisher } : {}),
|
|
326
|
-
revalidate: (input) => connectionService.revalidate(input),
|
|
327
|
-
});
|
|
328
|
-
}
|
|
329
|
-
// The per-USER override store is wired ONLY when its repository is present — which, by
|
|
330
|
-
// design, ONLY the local facade does (so per-user overrides + the per-user controller are
|
|
331
|
-
// local-mode-only, with no runtime branch in shared code). Its `resolveOverrides` is the
|
|
332
|
-
// `resolveUserHandlerOverrides` seam the provisioning service layers over the workspace
|
|
333
|
-
// handlers for the run initiator.
|
|
334
|
-
const userHandlerService = deps.environmentUserHandlerRepository
|
|
335
|
-
? new EnvironmentUserHandlerService({
|
|
336
|
-
userHandlerRepository: deps.environmentUserHandlerRepository,
|
|
337
|
-
environmentBackendRegistry: deps.environmentBackendRegistry ?? defaultEnvironmentBackendRegistry(),
|
|
338
|
-
secretCipher,
|
|
339
|
-
clock: deps.clock,
|
|
340
|
-
...(deps.environmentCustomTlsSupported !== undefined
|
|
341
|
-
? { customTlsSupported: deps.environmentCustomTlsSupported }
|
|
342
|
-
: {}),
|
|
343
|
-
...(deps.environmentUrlSafetyPolicy ? { urlPolicy: deps.environmentUrlSafetyPolicy } : {}),
|
|
344
|
-
...(deps.logger ? { logger: deps.logger } : {}),
|
|
345
|
-
})
|
|
346
|
-
: undefined;
|
|
347
|
-
// Built BEFORE the provisioning service so it can be injected as `environmentTeardown` there:
|
|
348
|
-
// a deployer re-run that supersedes a prior env with a DIFFERENT provider identity tears the old
|
|
349
|
-
// infra down through this service (best-effort; the TTL reaper is the backstop).
|
|
350
|
-
const teardownService = new EnvironmentTeardownService({
|
|
351
|
-
connectionService,
|
|
352
|
-
environmentRegistryRepository,
|
|
353
|
-
secretCipher,
|
|
354
|
-
clock: deps.clock,
|
|
355
|
-
...(provisioningLog ? { provisioningLog } : {}),
|
|
356
|
-
});
|
|
357
|
-
const provisioningService = new EnvironmentProvisioningService({
|
|
358
|
-
connectionService,
|
|
359
|
-
environmentRegistryRepository,
|
|
360
|
-
secretCipher,
|
|
361
|
-
idGenerator: deps.idGenerator,
|
|
362
|
-
clock: deps.clock,
|
|
363
|
-
environmentTeardown: teardownService,
|
|
364
|
-
...(deps.environmentUrlSafetyPolicy ? { urlPolicy: deps.environmentUrlSafetyPolicy } : {}),
|
|
365
|
-
...(deps.resolveRunRepoContext ? { resolveRunRepoContext: deps.resolveRunRepoContext } : {}),
|
|
366
|
-
...(deps.resolveRepoFilesForCoords
|
|
367
|
-
? { resolveRepoFilesForWorkspace: deps.resolveRepoFilesForCoords }
|
|
368
|
-
: {}),
|
|
369
|
-
...(userHandlerService
|
|
370
|
-
? {
|
|
371
|
-
resolveUserHandlerOverrides: (userId, ws) => userHandlerService.resolveOverrides(userId, ws),
|
|
372
|
-
}
|
|
373
|
-
: {}),
|
|
374
|
-
// The async, container-backed deploy lifecycle (kustomize/helm) is wired when the facade
|
|
375
|
-
// supplies the runner transport + the clone-target resolver; absent ⇒ only the synchronous
|
|
376
|
-
// raw-manifest REST path runs (a render-needing config fails loudly).
|
|
377
|
-
...(deps.deployJobClient ? { deployJobClient: deps.deployJobClient } : {}),
|
|
378
|
-
...(deps.resolveDeployCloneTarget
|
|
379
|
-
? { resolveDeployCloneTarget: deps.resolveDeployCloneTarget }
|
|
380
|
-
: {}),
|
|
381
|
-
// A compose stack recipe's `sharedStackRefs` are brought up (provider-before-consumer) through
|
|
382
|
-
// the shared-stack service, whose managed networks the compose provider attaches the per-PR
|
|
383
|
-
// project to. Wired only when the shared-stacks module exists (its repository is present on
|
|
384
|
-
// every facade); the lifecycle itself refuses without a host daemon.
|
|
385
|
-
...(sharedStackService
|
|
386
|
-
? { ensureSharedStacks: (ws, refs) => sharedStackService.ensureRefsUp(ws, refs) }
|
|
387
|
-
: {}),
|
|
388
|
-
// A compose stack recipe's `prerequisites` are re-run at provision start through the preflight
|
|
389
|
-
// service, whose host probes exist only on the local facade; absent ⇒ a recipe that declares
|
|
390
|
-
// them fails loudly instead of silently skipping a machine-prerequisite gate.
|
|
391
|
-
...(preflightService ? { runPreflights: (_ws, refs) => preflightService.run(refs) } : {}),
|
|
392
|
-
...(provisioningLog ? { provisioningLog } : {}),
|
|
393
|
-
});
|
|
394
|
-
// The ephemeral-environment self-test: needs its own run store + a git provider (to
|
|
395
|
-
// create/delete the throwaway branch). Absent either ⇒ no self-test (the controller 503s).
|
|
396
|
-
const environmentTest = deps.environmentTestRunRepository && deps.resolveRunRepoContext
|
|
397
|
-
? new EnvironmentTestService({
|
|
398
|
-
environmentTestRunRepository: deps.environmentTestRunRepository,
|
|
399
|
-
workspaceRepository: deps.workspaceRepository,
|
|
400
|
-
blockRepository: deps.blockRepository,
|
|
401
|
-
provisioning: provisioningService,
|
|
402
|
-
teardown: teardownService,
|
|
403
|
-
environmentRegistry: environmentRegistryRepository,
|
|
404
|
-
resolveRunRepoContext: deps.resolveRunRepoContext,
|
|
405
|
-
idGenerator: deps.idGenerator,
|
|
406
|
-
clock: deps.clock,
|
|
407
|
-
...(deps.environmentTestRunner ? { runner: deps.environmentTestRunner } : {}),
|
|
408
|
-
...(eventPublisher ? { eventPublisher } : {}),
|
|
409
|
-
})
|
|
410
|
-
: undefined;
|
|
411
|
-
return {
|
|
412
|
-
connectionService,
|
|
413
|
-
provisioningService,
|
|
414
|
-
teardownService,
|
|
415
|
-
...(userHandlerService ? { userHandlerService } : {}),
|
|
416
|
-
...(repairService ? { envConfigRepair: { service: repairService } } : {}),
|
|
417
|
-
...(environmentTest ? { environmentTest } : {}),
|
|
418
|
-
};
|
|
419
|
-
}
|
|
420
|
-
/**
|
|
421
|
-
* Assemble the self-hosted runner-pool module when its connection repository and
|
|
422
|
-
* the secret cipher are present; otherwise return undefined so the feature stays
|
|
423
|
-
* cleanly opt-in. Per-tenant scheduler-API secrets are encrypted via the cipher.
|
|
424
|
-
*/
|
|
425
|
-
function createRunnersModule(deps) {
|
|
426
|
-
const { runnerPoolConnectionRepository, runnerSecretCipher } = deps;
|
|
427
|
-
if (!runnerPoolConnectionRepository || !runnerSecretCipher)
|
|
428
|
-
return undefined;
|
|
429
|
-
const connectionService = new RunnerPoolConnectionService({
|
|
430
|
-
runnerPoolConnectionRepository,
|
|
431
|
-
workspaceRepository: deps.workspaceRepository,
|
|
432
|
-
secretCipher: runnerSecretCipher,
|
|
433
|
-
clock: deps.clock,
|
|
434
|
-
runnerBackendRegistry: deps.runnerBackendRegistry ?? defaultRunnerBackendRegistry(),
|
|
435
|
-
...(deps.runnerPoolProvider ? { runnerPoolProvider: deps.runnerPoolProvider } : {}),
|
|
436
|
-
...(deps.runnerUrlSafetyPolicy ? { urlPolicy: deps.runnerUrlSafetyPolicy } : {}),
|
|
437
|
-
...(deps.runnerCustomTlsSupported !== undefined
|
|
438
|
-
? { customTlsSupported: deps.runnerCustomTlsSupported }
|
|
439
|
-
: {}),
|
|
440
|
-
});
|
|
441
|
-
return { connectionService };
|
|
442
|
-
}
|
|
443
|
-
/**
|
|
444
|
-
* Assemble the repo-bootstrap module when both its repositories are present (the
|
|
445
|
-
* worker wires them unconditionally). The `repoBootstrapper` is passed through
|
|
446
|
-
* but optional: the service exposes CRUD regardless and only gates the run path
|
|
447
|
-
* on its presence.
|
|
448
|
-
*/
|
|
449
|
-
function createBootstrapModule(deps, eventPublisher, onBootstrapSucceeded) {
|
|
450
|
-
const { referenceArchitectureRepository, bootstrapJobRepository } = deps;
|
|
451
|
-
if (!referenceArchitectureRepository || !bootstrapJobRepository)
|
|
452
|
-
return undefined;
|
|
453
|
-
const service = new BootstrapService({
|
|
454
|
-
referenceArchitectureRepository,
|
|
455
|
-
bootstrapJobRepository,
|
|
456
|
-
workspaceRepository: deps.workspaceRepository,
|
|
457
|
-
blockRepository: deps.blockRepository,
|
|
458
|
-
serviceRepository: deps.serviceRepository,
|
|
459
|
-
workspaceMountRepository: deps.workspaceMountRepository,
|
|
460
|
-
serviceFragmentDefaultsRepository: deps.serviceFragmentDefaultsRepository,
|
|
461
|
-
idGenerator: deps.idGenerator,
|
|
462
|
-
clock: deps.clock,
|
|
463
|
-
repoBootstrapper: deps.repoBootstrapper,
|
|
464
|
-
bootstrapRunner: deps.bootstrapRunner,
|
|
465
|
-
eventPublisher,
|
|
466
|
-
...(onBootstrapSucceeded ? { onBootstrapSucceeded } : {}),
|
|
467
|
-
});
|
|
468
|
-
return { service };
|
|
469
|
-
}
|
|
470
|
-
/**
|
|
471
|
-
* Assemble the requirements-review module when its repository is present (the
|
|
472
|
-
* worker wires it unconditionally). The model provider/ref are optional within
|
|
473
|
-
* the module — reads work without them and the run paths surface a clear error —
|
|
474
|
-
* and the document/task repositories are reused, when wired, to fold linked PRDs
|
|
475
|
-
* and tracker issues into the reviewed requirements.
|
|
476
|
-
*/
|
|
477
|
-
/**
|
|
478
|
-
* Build the inline reviewer for the test quality-control companion. It resolves its model
|
|
479
|
-
* exactly like the requirements reviewer (block pin → workspace per-kind default → routing
|
|
480
|
-
* default). Returns `undefined` when no model provider is configured, so the Tester gate's QC
|
|
481
|
-
* step is a pass-through in unconfigured facades / tests.
|
|
482
|
-
*/
|
|
483
|
-
function createTesterQualityReviewer(deps) {
|
|
484
|
-
if (!deps.modelProviderResolver && !deps.modelProvider)
|
|
485
|
-
return undefined;
|
|
486
|
-
return new TesterQualityReviewService({
|
|
487
|
-
modelProviderResolver: deps.modelProviderResolver,
|
|
488
|
-
modelProvider: deps.modelProvider,
|
|
489
|
-
modelRef: deps.requirementReviewModel ?? deps.documentPlannerModel,
|
|
490
|
-
resolveBlockModel: deps.requirementReviewResolveModel,
|
|
491
|
-
...(deps.inlineHarnessRef ? { runsInline: deps.inlineHarnessRef } : {}),
|
|
492
|
-
resolveWorkspaceModelDefault: deps.modelPresetRepository
|
|
493
|
-
? (workspaceId, agentKind, modelPresetId) => resolvePresetModelForKind(deps.modelPresetRepository, workspaceId, agentKind, modelPresetId)
|
|
494
|
-
: undefined,
|
|
495
|
-
resolveRunContext: resolveBlockRunContext(deps),
|
|
496
|
-
});
|
|
497
|
-
}
|
|
498
|
-
/**
|
|
499
|
-
* Build the interactive document-interview service (WS5). Self-contained (owns its session
|
|
500
|
-
* store + the inline LLM); resolves its model exactly like the requirements reviewer (block
|
|
501
|
-
* pin → workspace per-kind default → routing default). Returns `undefined` when no session
|
|
502
|
-
* store is wired, so the `doc-interviewer` step passes through in unconfigured facades / tests.
|
|
503
|
-
* The LLM is optional within the service (the `enabled` getter is false without a model), so a
|
|
504
|
-
* store-but-no-model deployment still short-circuits the interviewer.
|
|
505
|
-
*/
|
|
506
|
-
function createDocInterviewService(deps) {
|
|
507
|
-
const { docInterviewRepository } = deps;
|
|
508
|
-
if (!docInterviewRepository)
|
|
509
|
-
return undefined;
|
|
510
|
-
return new DocInterviewService({
|
|
511
|
-
docInterviewRepository,
|
|
512
|
-
idGenerator: deps.idGenerator,
|
|
513
|
-
clock: deps.clock,
|
|
514
|
-
modelProviderResolver: deps.modelProviderResolver,
|
|
515
|
-
modelProvider: deps.modelProvider,
|
|
516
|
-
modelRef: deps.requirementReviewModel ?? deps.documentPlannerModel,
|
|
517
|
-
resolveBlockModel: deps.requirementReviewResolveModel,
|
|
518
|
-
...(deps.inlineHarnessRef ? { runsInline: deps.inlineHarnessRef } : {}),
|
|
519
|
-
resolveWorkspaceModelDefault: deps.modelPresetRepository
|
|
520
|
-
? (workspaceId, agentKind, modelPresetId) => resolvePresetModelForKind(deps.modelPresetRepository, workspaceId, agentKind, modelPresetId)
|
|
521
|
-
: undefined,
|
|
522
|
-
resolveRunContext: resolveBlockRunContext(deps),
|
|
523
|
-
});
|
|
524
|
-
}
|
|
525
|
-
/**
|
|
526
|
-
* Build the inline grounded-chat responder for the implementation-fork decision phase. Resolves
|
|
527
|
-
* its model exactly like the requirements reviewer / doc interviewer (block pin → workspace
|
|
528
|
-
* per-kind default → routing default). Returns `undefined` when no model provider is configured,
|
|
529
|
-
* so the fork chat degrades to a canned "chat unavailable" reply in unconfigured facades / tests
|
|
530
|
-
* while pick / custom keep working. Stateless — the chat rides the coder step, no session store.
|
|
531
|
-
*/
|
|
532
|
-
function createForkChatService(deps) {
|
|
533
|
-
if (!deps.modelProviderResolver && !deps.modelProvider)
|
|
534
|
-
return undefined;
|
|
535
|
-
return new ForkChatService({
|
|
536
|
-
modelProviderResolver: deps.modelProviderResolver,
|
|
537
|
-
modelProvider: deps.modelProvider,
|
|
538
|
-
modelRef: deps.requirementReviewModel ?? deps.documentPlannerModel,
|
|
539
|
-
resolveBlockModel: deps.requirementReviewResolveModel,
|
|
540
|
-
...(deps.inlineHarnessRef ? { runsInline: deps.inlineHarnessRef } : {}),
|
|
541
|
-
resolveWorkspaceModelDefault: deps.modelPresetRepository
|
|
542
|
-
? (workspaceId, agentKind, modelPresetId) => resolvePresetModelForKind(deps.modelPresetRepository, workspaceId, agentKind, modelPresetId)
|
|
543
|
-
: undefined,
|
|
544
|
-
resolveRunContext: resolveBlockRunContext(deps),
|
|
545
|
-
});
|
|
546
|
-
}
|
|
547
|
-
/**
|
|
548
|
-
* Resolve a block's active run (execution id + initiator) for the iterative reviewers, so an
|
|
549
|
-
* inline subscription reviewer served through a leased per-run activation can lease it. Reads
|
|
550
|
-
* the block's `executionId` and the run's `initiatedBy`; `{}` when the block has no active run
|
|
551
|
-
* (an off-path inspector review with no pipeline) — the reviewer then resolves on a
|
|
552
|
-
* workspace-only scope (pooled lease), unchanged.
|
|
553
|
-
*/
|
|
554
|
-
function resolveBlockRunContext(deps) {
|
|
555
|
-
return async (workspaceId, block) => {
|
|
556
|
-
if (!block.executionId)
|
|
557
|
-
return {};
|
|
558
|
-
const instance = await deps.executionRepository.get(workspaceId, block.executionId);
|
|
559
|
-
return {
|
|
560
|
-
executionId: block.executionId,
|
|
561
|
-
...(instance?.initiatedBy ? { userId: instance.initiatedBy } : {}),
|
|
562
|
-
};
|
|
563
|
-
};
|
|
564
|
-
}
|
|
565
|
-
function createRequirementsModule(deps, notificationService, fragmentLibrary) {
|
|
566
|
-
const { requirementReviewRepository } = deps;
|
|
567
|
-
if (!requirementReviewRepository)
|
|
568
|
-
return undefined;
|
|
569
|
-
const service = new RequirementReviewService({
|
|
570
|
-
requirementReviewRepository,
|
|
571
|
-
blockRepository: deps.blockRepository,
|
|
572
|
-
idGenerator: deps.idGenerator,
|
|
573
|
-
clock: deps.clock,
|
|
574
|
-
// Tell product people + the task creator to react to a review's findings (when
|
|
575
|
-
// the notifications subsystem is wired). Best-effort; absent → no notification.
|
|
576
|
-
notificationService,
|
|
577
|
-
modelProviderResolver: deps.modelProviderResolver,
|
|
578
|
-
modelProvider: deps.modelProvider,
|
|
579
|
-
// The dedicated reviewer ref, else the document planner's (both the agents' default).
|
|
580
|
-
modelRef: deps.requirementReviewModel ?? deps.documentPlannerModel,
|
|
581
|
-
// Honour a block's pinned model with the direct/Cloudflare fallback, like the executor.
|
|
582
|
-
resolveBlockModel: deps.requirementReviewResolveModel,
|
|
583
|
-
// In local mode, run the reviewer inline through the ambient Claude Code / Codex CLI on a
|
|
584
|
-
// subscription model instead of degrading to the routing default.
|
|
585
|
-
...(deps.inlineHarnessRef ? { runsInline: deps.inlineHarnessRef } : {}),
|
|
586
|
-
// Honour the workspace's model presets for the `requirements` kind too, so the
|
|
587
|
-
// reviewer resolves its model exactly like a pipeline step. Reuses the already
|
|
588
|
-
// wired model-preset repository (the workspace default preset); absent → only
|
|
589
|
-
// block-pin + routing default.
|
|
590
|
-
resolveWorkspaceModelDefault: deps.modelPresetRepository
|
|
591
|
-
? (workspaceId, agentKind, modelPresetId) => resolvePresetModelForKind(deps.modelPresetRepository, workspaceId, agentKind, modelPresetId)
|
|
592
|
-
: undefined,
|
|
593
|
-
// The reviewer runs during a parked run, so its execution + initiator come from the
|
|
594
|
-
// block's active run — threaded into the model scope so an inline subscription ref served
|
|
595
|
-
// through a leased per-run activation (local container inline backend) can lease it.
|
|
596
|
-
resolveRunContext: resolveBlockRunContext(deps),
|
|
597
|
-
documentRepository: deps.documentRepository,
|
|
598
|
-
taskRepository: deps.taskRepository,
|
|
599
|
-
// The Requirement Writer (second companion) grounds recommendations on the run's repo
|
|
600
|
-
// (`spec/` + `tech-spec/` via the checkout-free RepoFiles) — wired in all three facades.
|
|
601
|
-
resolveRunRepoContext: deps.resolveRunRepoContext,
|
|
602
|
-
// …and on the block's best-practice fragments (team/org standards), checked FIRST. Walk
|
|
603
|
-
// the owning frame's service standards then union the block's own pins (same precedence
|
|
604
|
-
// as the agent context builder), resolved against the merged tenant catalog when the
|
|
605
|
-
// fragment library is wired (so managed + document-backed fragments ground the review
|
|
606
|
-
// exactly like they reach a code-aware run), else the static universal pool.
|
|
607
|
-
resolveBlockFragments: async (workspaceId, blockId) => {
|
|
608
|
-
const block = await deps.blockRepository.get(workspaceId, blockId);
|
|
609
|
-
if (!block)
|
|
610
|
-
return [];
|
|
611
|
-
const ids = [];
|
|
612
|
-
const seen = new Set();
|
|
613
|
-
const add = (id) => {
|
|
614
|
-
if (!seen.has(id)) {
|
|
615
|
-
seen.add(id);
|
|
616
|
-
ids.push(id);
|
|
617
|
-
}
|
|
618
|
-
};
|
|
619
|
-
let current = block;
|
|
620
|
-
for (let i = 0; current && i < 8; i++) {
|
|
621
|
-
if (current.level === 'frame' || !current.parentId) {
|
|
622
|
-
for (const id of current.serviceFragmentIds ?? [])
|
|
623
|
-
add(id);
|
|
624
|
-
break;
|
|
625
|
-
}
|
|
626
|
-
current = await deps.blockRepository.get(workspaceId, current.parentId);
|
|
627
|
-
}
|
|
628
|
-
for (const id of block.fragmentIds ?? [])
|
|
629
|
-
add(id);
|
|
630
|
-
if (fragmentLibrary) {
|
|
631
|
-
// Resolve the merged tenant catalog ONCE and reuse it for both the titles map and
|
|
632
|
-
// the body resolution (which would otherwise re-resolve the same catalog).
|
|
633
|
-
const catalog = await fragmentLibrary.libraryService.resolveCatalog(workspaceId);
|
|
634
|
-
const titles = new Map(catalog.map((e) => [e.id, e.title]));
|
|
635
|
-
const bodies = await fragmentLibrary.libraryService.resolveBodiesForRun(workspaceId, ids, catalog);
|
|
636
|
-
return bodies.map(({ id, body }) => ({ id, title: titles.get(id) ?? id, body }));
|
|
637
|
-
}
|
|
638
|
-
const out = [];
|
|
639
|
-
for (const id of ids) {
|
|
640
|
-
const fragment = getFragment(id);
|
|
641
|
-
if (fragment)
|
|
642
|
-
out.push({ id, title: fragment.title, body: fragment.body });
|
|
643
|
-
}
|
|
644
|
-
return out;
|
|
645
|
-
},
|
|
646
|
-
// `webSearch` (gateway-RAG) is wired by the web-search-connection workstream; until then
|
|
647
|
-
// the Writer still gets provider-hosted web search on Anthropic/OpenAI models.
|
|
648
|
-
// When an upstream `requirements-brainstorm` dialogue settled a converged direction, the
|
|
649
|
-
// reviewer critiques THAT (the refined requirements) instead of the raw description.
|
|
650
|
-
resolveBrainstormDirection: deps.brainstormSessionRepository
|
|
651
|
-
? async (workspaceId, blockId) => {
|
|
652
|
-
const session = await deps.brainstormSessionRepository.getByBlockStage(workspaceId, blockId, 'requirements');
|
|
653
|
-
return session?.status === 'incorporated' && session.convergedDirection
|
|
654
|
-
? session.convergedDirection
|
|
655
|
-
: undefined;
|
|
656
|
-
}
|
|
657
|
-
: undefined,
|
|
658
|
-
});
|
|
659
|
-
return { service };
|
|
660
|
-
}
|
|
661
|
-
/**
|
|
662
|
-
* Assemble the brainstorm (structured-dialogue) module when its repository is present (both
|
|
663
|
-
* runtime facades wire it unconditionally). Mirrors {@link createClarityModule}: it builds ONE
|
|
664
|
-
* {@link BrainstormService} per stage (sharing the repository) and reuses the requirements
|
|
665
|
-
* reviewer's model config since all the inline reviewers resolve their model identically. The
|
|
666
|
-
* architecture stage seeds from the refined requirements (a requirements review's incorporated
|
|
667
|
-
* doc, else the requirements-brainstorm's converged direction).
|
|
668
|
-
*/
|
|
669
|
-
function createBrainstormModule(deps, notificationService) {
|
|
670
|
-
const { brainstormSessionRepository } = deps;
|
|
671
|
-
if (!brainstormSessionRepository)
|
|
672
|
-
return undefined;
|
|
673
|
-
const resolveWorkspaceModelDefault = deps.modelPresetRepository
|
|
674
|
-
? (workspaceId, agentKind, modelPresetId) => resolvePresetModelForKind(deps.modelPresetRepository, workspaceId, agentKind, modelPresetId)
|
|
675
|
-
: undefined;
|
|
676
|
-
// The architecture stage's seed: the most refined requirements available — a settled
|
|
677
|
-
// requirements review's incorporated doc, else the requirements-brainstorm's direction.
|
|
678
|
-
const resolveRefinedRequirements = async (workspaceId, blockId) => {
|
|
679
|
-
const review = await deps.requirementReviewRepository?.getByBlock(workspaceId, blockId);
|
|
680
|
-
if (review?.status === 'incorporated' && review.incorporatedRequirements) {
|
|
681
|
-
return review.incorporatedRequirements;
|
|
682
|
-
}
|
|
683
|
-
const session = await brainstormSessionRepository.getByBlockStage(workspaceId, blockId, 'requirements');
|
|
684
|
-
return session?.status === 'incorporated' && session.convergedDirection
|
|
685
|
-
? session.convergedDirection
|
|
686
|
-
: undefined;
|
|
687
|
-
};
|
|
688
|
-
const common = {
|
|
689
|
-
brainstormSessionRepository,
|
|
690
|
-
blockRepository: deps.blockRepository,
|
|
691
|
-
idGenerator: deps.idGenerator,
|
|
692
|
-
clock: deps.clock,
|
|
693
|
-
notificationService,
|
|
694
|
-
modelProviderResolver: deps.modelProviderResolver,
|
|
695
|
-
modelProvider: deps.modelProvider,
|
|
696
|
-
modelRef: deps.requirementReviewModel ?? deps.documentPlannerModel,
|
|
697
|
-
resolveBlockModel: deps.requirementReviewResolveModel,
|
|
698
|
-
...(deps.inlineHarnessRef ? { runsInline: deps.inlineHarnessRef } : {}),
|
|
699
|
-
// Brainstorm stages are pipeline gate steps that run during a parked run, so their
|
|
700
|
-
// execution + initiator come from the block's active run — threaded into the model scope
|
|
701
|
-
// so an inline subscription ref served through a leased per-run activation (local container
|
|
702
|
-
// inline backend) can lease it, exactly like the requirements/clarity reviewers.
|
|
703
|
-
resolveRunContext: resolveBlockRunContext(deps),
|
|
704
|
-
resolveWorkspaceModelDefault,
|
|
705
|
-
};
|
|
706
|
-
return {
|
|
707
|
-
services: {
|
|
708
|
-
requirements: new BrainstormService({ ...common, stage: 'requirements' }),
|
|
709
|
-
architecture: new BrainstormService({
|
|
710
|
-
...common,
|
|
711
|
-
stage: 'architecture',
|
|
712
|
-
resolveRefinedRequirements,
|
|
713
|
-
}),
|
|
714
|
-
},
|
|
715
|
-
};
|
|
716
|
-
}
|
|
717
|
-
/**
|
|
718
|
-
* Assemble the Kaizen module when its repositories are wired (both runtime facades wire them
|
|
719
|
-
* unconditionally). The grader resolves its model for the `kaizen` kind the same way the
|
|
720
|
-
* requirements reviewer does — block pin > workspace per-kind default > routing default —
|
|
721
|
-
* so operators configure it in Model Configuration alongside every other agent. Needs the
|
|
722
|
-
* telemetry repos (LLM-call metrics + agent-context snapshots) to read what each step was
|
|
723
|
-
* given; absent → the module isn't built and no grading is scheduled.
|
|
724
|
-
*/
|
|
725
|
-
function createKaizenModule(deps) {
|
|
726
|
-
const { kaizenGradingRepository, kaizenVerifiedComboRepository } = deps;
|
|
727
|
-
if (!kaizenGradingRepository || !kaizenVerifiedComboRepository)
|
|
728
|
-
return undefined;
|
|
729
|
-
if (!deps.llmCallMetricRepository || !deps.agentContextObservability)
|
|
730
|
-
return undefined;
|
|
731
|
-
const service = new KaizenService({
|
|
732
|
-
kaizenGradingRepository,
|
|
733
|
-
kaizenVerifiedComboRepository,
|
|
734
|
-
blockRepository: deps.blockRepository,
|
|
735
|
-
llmCallMetricRepository: deps.llmCallMetricRepository,
|
|
736
|
-
agentContextObservability: deps.agentContextObservability,
|
|
737
|
-
workspaceSettingsRepository: deps.workspaceSettingsRepository,
|
|
738
|
-
idGenerator: deps.idGenerator,
|
|
739
|
-
clock: deps.clock,
|
|
740
|
-
events: deps.executionEventPublisher,
|
|
741
|
-
modelProviderResolver: deps.modelProviderResolver,
|
|
742
|
-
modelProvider: deps.modelProvider,
|
|
743
|
-
// Reuse the reviewer's routing default ref + block-model resolver (the agents' default).
|
|
744
|
-
modelRef: deps.requirementReviewModel ?? deps.documentPlannerModel,
|
|
745
|
-
resolveBlockModel: deps.requirementReviewResolveModel,
|
|
746
|
-
...(deps.inlineHarnessRef ? { runsInline: deps.inlineHarnessRef } : {}),
|
|
747
|
-
// Resolve the workspace's per-kind default for `kaizen`, like a pipeline step.
|
|
748
|
-
resolveWorkspaceModelDefault: deps.modelPresetRepository
|
|
749
|
-
? (workspaceId, agentKind, modelPresetId) => resolvePresetModelForKind(deps.modelPresetRepository, workspaceId, agentKind, modelPresetId)
|
|
750
|
-
: undefined,
|
|
751
|
-
});
|
|
752
|
-
return { service };
|
|
753
|
-
}
|
|
754
|
-
/**
|
|
755
|
-
* Assemble the clarity-review module when its repository is present (both runtime facades
|
|
756
|
-
* wire it unconditionally). Mirrors {@link createRequirementsModule}: it reuses the
|
|
757
|
-
* requirements reviewer's model config (the same routing default) since both reviewers
|
|
758
|
-
* resolve their model identically.
|
|
759
|
-
*/
|
|
760
|
-
function createClarityModule(deps, notificationService) {
|
|
761
|
-
const { clarityReviewRepository } = deps;
|
|
762
|
-
if (!clarityReviewRepository)
|
|
763
|
-
return undefined;
|
|
764
|
-
const service = new ClarityReviewService({
|
|
765
|
-
clarityReviewRepository,
|
|
766
|
-
blockRepository: deps.blockRepository,
|
|
767
|
-
idGenerator: deps.idGenerator,
|
|
768
|
-
clock: deps.clock,
|
|
769
|
-
notificationService,
|
|
770
|
-
modelProviderResolver: deps.modelProviderResolver,
|
|
771
|
-
modelProvider: deps.modelProvider,
|
|
772
|
-
modelRef: deps.requirementReviewModel ?? deps.documentPlannerModel,
|
|
773
|
-
resolveBlockModel: deps.requirementReviewResolveModel,
|
|
774
|
-
...(deps.inlineHarnessRef ? { runsInline: deps.inlineHarnessRef } : {}),
|
|
775
|
-
resolveWorkspaceModelDefault: deps.modelPresetRepository
|
|
776
|
-
? (workspaceId, agentKind, modelPresetId) => resolvePresetModelForKind(deps.modelPresetRepository, workspaceId, agentKind, modelPresetId)
|
|
777
|
-
: undefined,
|
|
778
|
-
resolveRunContext: resolveBlockRunContext(deps),
|
|
779
|
-
});
|
|
780
|
-
return { service };
|
|
781
|
-
}
|
|
782
|
-
/**
|
|
783
|
-
* Assemble the notifications module when its repository is present (the worker
|
|
784
|
-
* wires it unconditionally). The delivery channel is optional within the module —
|
|
785
|
-
* without it the rows still persist (the inbox + snapshot work) but nothing is
|
|
786
|
-
* pushed; the worker wires the in-app channel, and email/Slack compose in later.
|
|
787
|
-
*/
|
|
788
|
-
function createNotificationsModule(deps) {
|
|
789
|
-
const { notificationRepository } = deps;
|
|
790
|
-
if (!notificationRepository)
|
|
791
|
-
return undefined;
|
|
792
|
-
const service = new NotificationService({
|
|
793
|
-
notificationRepository,
|
|
794
|
-
workspaceRepository: deps.workspaceRepository,
|
|
795
|
-
idGenerator: deps.idGenerator,
|
|
796
|
-
clock: deps.clock,
|
|
797
|
-
channel: deps.notificationChannel,
|
|
798
|
-
});
|
|
799
|
-
return { service };
|
|
800
|
-
}
|
|
801
|
-
/**
|
|
802
|
-
* Assemble the Slack integration module when its three repositories and the
|
|
803
|
-
* secret cipher are present. Powers the management API (connect/settings/member
|
|
804
|
-
* map); the actual Slack delivery is a `notificationChannel` composed in by the
|
|
805
|
-
* facade. OAuth is optional — manual-token onboarding works without it.
|
|
806
|
-
*/
|
|
807
|
-
function createSlackModule(deps) {
|
|
808
|
-
const { slackConnectionRepository, slackSettingsRepository, slackMemberMappingRepository, slackSecretCipher, } = deps;
|
|
809
|
-
if (!slackConnectionRepository ||
|
|
810
|
-
!slackSettingsRepository ||
|
|
811
|
-
!slackMemberMappingRepository ||
|
|
812
|
-
!slackSecretCipher) {
|
|
813
|
-
return undefined;
|
|
814
|
-
}
|
|
815
|
-
return {
|
|
816
|
-
connectionService: new SlackConnectionService({
|
|
817
|
-
slackConnectionRepository,
|
|
818
|
-
workspaceRepository: deps.workspaceRepository,
|
|
819
|
-
secretCipher: slackSecretCipher,
|
|
820
|
-
clock: deps.clock,
|
|
821
|
-
resolveOAuth: deps.accountSettings
|
|
822
|
-
? (accountKey) => deps.accountSettings.resolve(accountKey).then((s) => s.slackOAuth)
|
|
823
|
-
: undefined,
|
|
824
|
-
}),
|
|
825
|
-
settingsService: new SlackSettingsService({
|
|
826
|
-
slackSettingsRepository,
|
|
827
|
-
workspaceRepository: deps.workspaceRepository,
|
|
828
|
-
clock: deps.clock,
|
|
829
|
-
}),
|
|
830
|
-
memberMappingService: new SlackMemberMappingService({
|
|
831
|
-
slackMemberMappingRepository,
|
|
832
|
-
workspaceRepository: deps.workspaceRepository,
|
|
833
|
-
clock: deps.clock,
|
|
834
|
-
}),
|
|
835
|
-
};
|
|
836
|
-
}
|
|
837
|
-
/** Assemble the merge-preset module when its repository is present. */
|
|
838
|
-
function createRiskPoliciesModule(deps, caches) {
|
|
839
|
-
const { riskPolicyRepository } = deps;
|
|
840
|
-
if (!riskPolicyRepository)
|
|
841
|
-
return undefined;
|
|
842
|
-
const service = new RiskPolicyService({
|
|
843
|
-
riskPolicyRepository,
|
|
844
|
-
workspaceRepository: deps.workspaceRepository,
|
|
845
|
-
idGenerator: deps.idGenerator,
|
|
846
|
-
clock: deps.clock,
|
|
847
|
-
// Invalidate the read-through slice the engine's `resolveRiskPolicy` uses on every write.
|
|
848
|
-
riskPolicyCache: caches.riskPolicy,
|
|
849
|
-
});
|
|
850
|
-
return { service };
|
|
851
|
-
}
|
|
852
|
-
/**
|
|
853
|
-
* Assemble the shared-stacks module when its repository is present. The `composeRuntime` is
|
|
854
|
-
* optional — wired only on the local facade, so CRUD works everywhere but the lifecycle
|
|
855
|
-
* (ensureUp/teardown) refuses without a host daemon (the documented compose runtime-binding
|
|
856
|
-
* exception). Persistence is fully runtime-symmetric.
|
|
857
|
-
*/
|
|
858
|
-
function createSharedStacksModule(deps, preflightService) {
|
|
859
|
-
const { sharedStackRepository } = deps;
|
|
860
|
-
if (!sharedStackRepository)
|
|
861
|
-
return undefined;
|
|
862
|
-
const service = new SharedStackService({
|
|
863
|
-
sharedStackRepository,
|
|
864
|
-
workspaceRepository: deps.workspaceRepository,
|
|
865
|
-
idGenerator: deps.idGenerator,
|
|
866
|
-
clock: deps.clock,
|
|
867
|
-
...(deps.composeRuntime ? { composeRuntime: deps.composeRuntime } : {}),
|
|
868
|
-
...(deps.sharedStackCloneToken ? { cloneToken: deps.sharedStackCloneToken } : {}),
|
|
869
|
-
// Enables the checkout-free repo autodetection (`detect`); wired from the same coords-bound
|
|
870
|
-
// RepoFiles resolver the environment detector uses, so both facades get it for free.
|
|
871
|
-
...(deps.resolveRepoFilesForCoords
|
|
872
|
-
? { resolveRepoFilesForWorkspace: deps.resolveRepoFilesForCoords }
|
|
873
|
-
: {}),
|
|
874
|
-
// Same deployment-level detection-convention extensions the environment detector honours, so
|
|
875
|
-
// shared-stack `detect` recognises the org's house compose layout too.
|
|
876
|
-
...(deps.detectionConventions ? { detectionConventions: deps.detectionConventions } : {}),
|
|
877
|
-
// Re-run a stack's declared machine-prerequisite checks at bring-up start. Present only where
|
|
878
|
-
// the host-probe seam is wired (the local facade — same runtime binding as `composeRuntime`).
|
|
879
|
-
...(preflightService ? { runPreflights: (refs) => preflightService.run(refs) } : {}),
|
|
880
|
-
});
|
|
881
|
-
return { service };
|
|
882
|
-
}
|
|
883
|
-
/**
|
|
884
|
-
* Assemble the preflight module when the host-probe seam is present — wired ONLY on the local
|
|
885
|
-
* facade (a host Docker daemon), the documented compose runtime-binding exception. Absent elsewhere
|
|
886
|
-
* ⇒ the preflight API 503s and a stack recipe that declares `prerequisites` fails loudly at
|
|
887
|
-
* provision (rather than silently skipping a declared machine-prerequisite gate).
|
|
888
|
-
*/
|
|
889
|
-
function createPreflightModule(deps) {
|
|
890
|
-
if (!deps.preflightHostProbes)
|
|
891
|
-
return undefined;
|
|
892
|
-
return { service: new PreflightService({ hostProbes: deps.preflightHostProbes }) };
|
|
893
|
-
}
|
|
894
|
-
/**
|
|
895
|
-
* Assemble the Sandbox module when its five repositories are present (both runtime
|
|
896
|
-
* facades wire them together). Reuses the requirements reviewer's inline model config —
|
|
897
|
-
* the per-scope provider resolver, the routing default ref, and the block-model resolver
|
|
898
|
-
* — so a Sandbox cell (and the judge) resolves its catalog id exactly like a pipeline step.
|
|
899
|
-
*/
|
|
900
|
-
function createSandboxModule(deps, agentKindRegistry) {
|
|
901
|
-
const { sandboxPromptVersionRepository, sandboxFixtureRepository, sandboxExperimentRepository, sandboxRunRepository, sandboxGradeRepository, } = deps;
|
|
902
|
-
if (!sandboxPromptVersionRepository ||
|
|
903
|
-
!sandboxFixtureRepository ||
|
|
904
|
-
!sandboxExperimentRepository ||
|
|
905
|
-
!sandboxRunRepository ||
|
|
906
|
-
!sandboxGradeRepository) {
|
|
907
|
-
return undefined;
|
|
908
|
-
}
|
|
909
|
-
const repositories = {
|
|
910
|
-
sandboxPromptVersionRepository,
|
|
911
|
-
sandboxFixtureRepository,
|
|
912
|
-
sandboxExperimentRepository,
|
|
913
|
-
sandboxRunRepository,
|
|
914
|
-
sandboxGradeRepository,
|
|
915
|
-
workspaceRepository: deps.workspaceRepository,
|
|
916
|
-
idGenerator: deps.idGenerator,
|
|
917
|
-
clock: deps.clock,
|
|
918
|
-
agentKindRegistry,
|
|
919
|
-
};
|
|
920
|
-
const defaultModelRef = deps.requirementReviewModel ?? deps.documentPlannerModel;
|
|
921
|
-
const service = new SandboxService({ ...repositories, defaultModelRef });
|
|
922
|
-
const runService = new SandboxRunService({
|
|
923
|
-
...repositories,
|
|
924
|
-
modelProviderResolver: deps.modelProviderResolver,
|
|
925
|
-
modelProvider: deps.modelProvider,
|
|
926
|
-
resolveModelId: deps.requirementReviewResolveModel,
|
|
927
|
-
defaultModelRef,
|
|
928
|
-
...(deps.inlineHarnessRef ? { runsInline: deps.inlineHarnessRef } : {}),
|
|
929
|
-
});
|
|
930
|
-
return { service, runService };
|
|
931
|
-
}
|
|
932
|
-
/** Assemble the workspace-settings module when its repository is present. */
|
|
933
|
-
function createWorkspaceSettingsModule(deps, workspaceSettingsCache) {
|
|
934
|
-
const { workspaceSettingsRepository } = deps;
|
|
935
|
-
if (!workspaceSettingsRepository)
|
|
936
|
-
return undefined;
|
|
937
|
-
const service = new WorkspaceSettingsService({
|
|
938
|
-
workspaceSettingsRepository,
|
|
939
|
-
workspaceRepository: deps.workspaceRepository,
|
|
940
|
-
workspaceSettingsCache,
|
|
941
|
-
});
|
|
942
|
-
return { service };
|
|
943
|
-
}
|
|
944
|
-
/** Assemble the release-health (observability) module when its repos + cipher are present. */
|
|
945
|
-
function createReleaseHealthModule(deps) {
|
|
946
|
-
const { observabilityConnectionRepository, releaseHealthConfigRepository, observabilitySecretCipher, } = deps;
|
|
947
|
-
if (!observabilityConnectionRepository ||
|
|
948
|
-
!releaseHealthConfigRepository ||
|
|
949
|
-
!observabilitySecretCipher) {
|
|
950
|
-
return undefined;
|
|
951
|
-
}
|
|
952
|
-
const service = new ReleaseHealthService({
|
|
953
|
-
observabilityConnectionRepository,
|
|
954
|
-
releaseHealthConfigRepository,
|
|
955
|
-
observabilitySecretCipher,
|
|
956
|
-
workspaceRepository: deps.workspaceRepository,
|
|
957
|
-
blockRepository: deps.blockRepository,
|
|
958
|
-
clock: deps.clock,
|
|
959
|
-
});
|
|
960
|
-
return { service };
|
|
961
|
-
}
|
|
962
|
-
/** Assemble the package-registries module when its repo + cipher are present. */
|
|
963
|
-
function createPackageRegistriesModule(deps) {
|
|
964
|
-
const { packageRegistryConnectionRepository, packageRegistrySecretCipher } = deps;
|
|
965
|
-
if (!packageRegistryConnectionRepository || !packageRegistrySecretCipher) {
|
|
966
|
-
return undefined;
|
|
967
|
-
}
|
|
968
|
-
const service = new PackageRegistryService({
|
|
969
|
-
packageRegistryConnectionRepository,
|
|
970
|
-
packageRegistrySecretCipher,
|
|
971
|
-
workspaceRepository: deps.workspaceRepository,
|
|
972
|
-
clock: deps.clock,
|
|
973
|
-
idGenerator: deps.idGenerator,
|
|
974
|
-
});
|
|
975
|
-
return { service };
|
|
976
|
-
}
|
|
977
|
-
/**
|
|
978
|
-
* Assemble the browsable-frontend-preview module when its per-runtime transport + the facade's
|
|
979
|
-
* job builder + the env registry are all wired (local/node with a host-port-publish runtime).
|
|
980
|
-
* Absent on the Worker (no preview transport) ⇒ the controller 503s there.
|
|
981
|
-
*/
|
|
982
|
-
function createPreviewModule(deps) {
|
|
983
|
-
const { previewTransport, buildPreviewJob, environmentRegistryRepository } = deps;
|
|
984
|
-
if (!previewTransport || !buildPreviewJob || !environmentRegistryRepository)
|
|
985
|
-
return undefined;
|
|
986
|
-
const service = new PreviewService({
|
|
987
|
-
previewTransport,
|
|
988
|
-
buildPreviewJob,
|
|
989
|
-
environmentRegistryRepository,
|
|
990
|
-
idGenerator: deps.idGenerator,
|
|
991
|
-
clock: deps.clock,
|
|
992
|
-
});
|
|
993
|
-
return { service };
|
|
994
|
-
}
|
|
995
|
-
/** Assemble the incident-enrichment settings module when its repo + cipher are present. */
|
|
996
|
-
function createIncidentEnrichmentModule(deps) {
|
|
997
|
-
const { incidentEnrichmentConnectionRepository, incidentEnrichmentSecretCipher } = deps;
|
|
998
|
-
if (!incidentEnrichmentConnectionRepository || !incidentEnrichmentSecretCipher)
|
|
999
|
-
return undefined;
|
|
1000
|
-
const service = new IncidentEnrichmentService({
|
|
1001
|
-
incidentEnrichmentConnectionRepository,
|
|
1002
|
-
incidentEnrichmentSecretCipher,
|
|
1003
|
-
workspaceRepository: deps.workspaceRepository,
|
|
1004
|
-
clock: deps.clock,
|
|
1005
|
-
});
|
|
1006
|
-
return { service };
|
|
1007
|
-
}
|
|
1008
|
-
/** Assemble the model-presets module when its repository is present. */
|
|
1009
|
-
function createModelPresetsModule(deps) {
|
|
1010
|
-
const { modelPresetRepository } = deps;
|
|
1011
|
-
if (!modelPresetRepository)
|
|
1012
|
-
return undefined;
|
|
1013
|
-
const service = new ModelPresetService({
|
|
1014
|
-
modelPresetRepository,
|
|
1015
|
-
workspaceRepository: deps.workspaceRepository,
|
|
1016
|
-
idGenerator: deps.idGenerator,
|
|
1017
|
-
clock: deps.clock,
|
|
1018
|
-
...(deps.defaultModelPresetId ? { defaultPresetId: deps.defaultModelPresetId } : {}),
|
|
1019
|
-
});
|
|
1020
|
-
return { service };
|
|
1021
|
-
}
|
|
1022
|
-
/** Assemble the service-fragment-defaults module when its repository is present. */
|
|
1023
|
-
function createServiceFragmentDefaultsModule(deps) {
|
|
1024
|
-
const { serviceFragmentDefaultsRepository } = deps;
|
|
1025
|
-
if (!serviceFragmentDefaultsRepository)
|
|
1026
|
-
return undefined;
|
|
1027
|
-
const service = new ServiceFragmentDefaultsService({
|
|
1028
|
-
serviceFragmentDefaultsRepository,
|
|
1029
|
-
workspaceRepository: deps.workspaceRepository,
|
|
1030
|
-
});
|
|
1031
|
-
return { service };
|
|
1032
|
-
}
|
|
1033
|
-
/** Assemble the tracker-settings module when its repository is present. */
|
|
1034
|
-
function createTrackerModule(deps) {
|
|
1035
|
-
const { trackerSettingsRepository } = deps;
|
|
1036
|
-
if (!trackerSettingsRepository)
|
|
1037
|
-
return undefined;
|
|
1038
|
-
const service = new TrackerSettingsService({
|
|
1039
|
-
trackerSettingsRepository,
|
|
1040
|
-
workspaceRepository: deps.workspaceRepository,
|
|
1041
|
-
clock: deps.clock,
|
|
1042
|
-
});
|
|
1043
|
-
return { service };
|
|
1044
|
-
}
|
|
1045
|
-
/**
|
|
1046
|
-
* Assemble the recurring-pipeline module when its repository is present. Built
|
|
1047
|
-
* after the execution engine since each fire starts a pipeline through it.
|
|
1048
|
-
*/
|
|
1049
|
-
function createRecurringModule(deps, executionService, executionEventPublisher, taskConnectionService) {
|
|
1050
|
-
const { pipelineScheduleRepository } = deps;
|
|
1051
|
-
if (!pipelineScheduleRepository)
|
|
1052
|
-
return undefined;
|
|
1053
|
-
const service = new RecurringPipelineService({
|
|
1054
|
-
pipelineScheduleRepository,
|
|
1055
|
-
workspaceRepository: deps.workspaceRepository,
|
|
1056
|
-
pipelineRepository: deps.pipelineRepository,
|
|
1057
|
-
blockRepository: deps.blockRepository,
|
|
1058
|
-
executionRepository: deps.executionRepository,
|
|
1059
|
-
executionService,
|
|
1060
|
-
idGenerator: deps.idGenerator,
|
|
1061
|
-
clock: deps.clock,
|
|
1062
|
-
serviceRepository: deps.serviceRepository,
|
|
1063
|
-
workspaceMountRepository: deps.workspaceMountRepository,
|
|
1064
|
-
// Validates a `bug-intake` pipeline's schedule carries an `issueIntake` config whose source
|
|
1065
|
-
// is a connected task source. Absent (no task sources wired) → the presence check still runs.
|
|
1066
|
-
taskConnectionService,
|
|
1067
|
-
// Pushes a `block-added` board event when the reused block is created, so it appears live.
|
|
1068
|
-
executionEventPublisher,
|
|
1069
|
-
});
|
|
1070
|
-
return { service };
|
|
1071
|
-
}
|
|
1072
54
|
export function createCore(dependencies) {
|
|
1073
55
|
// Resolve the app-owned agent-kind registry ONCE: the facade's injected instance (so a
|
|
1074
56
|
// deployment's custom kinds are visible) else a fresh built-ins-only registry. The SAME
|
|
@@ -1090,6 +72,15 @@ export function createCore(dependencies) {
|
|
|
1090
72
|
// write-site invalidation — is exercised everywhere. Built here (before the services that
|
|
1091
73
|
// invalidate through it) so it can be threaded into all of them.
|
|
1092
74
|
const caches = dependencies.caches ?? createAppCaches();
|
|
75
|
+
// The optional-module registry: every feature that is wired only when its prerequisites are
|
|
76
|
+
// configured is `build`-declared through this, instead of a scattered `const x = createX(...)`
|
|
77
|
+
// + a matching `...(x ? { x } : {})` return spread. Registration order below IS dependency
|
|
78
|
+
// order: `build` returns the value, so a module consumed downstream is kept in a local and
|
|
79
|
+
// threaded into the later factories that need it (`modules.get(...)` is there for a reader that
|
|
80
|
+
// holds no local). The whole set is emitted in one place via `...modules.assemble()` at the
|
|
81
|
+
// return. The core spine (below) stays explicit —
|
|
82
|
+
// it carries the genuine circular late-bindings (account ⇄ spend, engine ⇄ initiative loop).
|
|
83
|
+
const modules = new ModuleRegistry();
|
|
1093
84
|
// Pass the resolved publisher so board mutations push a coarse `boardChanged` to every
|
|
1094
85
|
// user on the workspace (and every board mounting a shared service) — both facades route
|
|
1095
86
|
// here, so the wiring is symmetric by construction. The repo-projection cache lets
|
|
@@ -1107,7 +98,7 @@ export function createCore(dependencies) {
|
|
|
1107
98
|
// Workspace-RBAC roster + access-mode management (workspace-rbac, slice 5). Present only when
|
|
1108
99
|
// the member repository is wired (both facades wire it; tests/no-roster leave it absent, so the
|
|
1109
100
|
// members controller 503s). Every roster/access-mode write drops the board's access cache group.
|
|
1110
|
-
|
|
101
|
+
modules.build('workspaceMemberService', () => dependencies.workspaceMemberRepository
|
|
1111
102
|
? new WorkspaceMemberService({
|
|
1112
103
|
workspaceMemberRepository: dependencies.workspaceMemberRepository,
|
|
1113
104
|
workspaceRepository: dependencies.workspaceRepository,
|
|
@@ -1116,7 +107,7 @@ export function createCore(dependencies) {
|
|
|
1116
107
|
clock: dependencies.clock,
|
|
1117
108
|
workspaceAccessCache: caches.workspaceAccess,
|
|
1118
109
|
})
|
|
1119
|
-
: undefined;
|
|
110
|
+
: undefined);
|
|
1120
111
|
// Late-bound so the account service can invalidate the spend service's cached
|
|
1121
112
|
// account-budget limit on an account-budget edit (spendService is built below).
|
|
1122
113
|
let spendServiceRef;
|
|
@@ -1140,14 +131,14 @@ export function createCore(dependencies) {
|
|
|
1140
131
|
idGenerator: dependencies.idGenerator,
|
|
1141
132
|
clock: dependencies.clock,
|
|
1142
133
|
});
|
|
1143
|
-
const email = dependencies.emailConnectionRepository && dependencies.emailSecretCipher
|
|
134
|
+
const email = modules.build('email', () => dependencies.emailConnectionRepository && dependencies.emailSecretCipher
|
|
1144
135
|
? new EmailConnectionService({
|
|
1145
136
|
emailConnectionRepository: dependencies.emailConnectionRepository,
|
|
1146
137
|
secretCipher: dependencies.emailSecretCipher,
|
|
1147
138
|
clock: dependencies.clock,
|
|
1148
139
|
})
|
|
1149
|
-
: undefined;
|
|
1150
|
-
|
|
140
|
+
: undefined);
|
|
141
|
+
modules.build('invitations', () => dependencies.invitationRepository
|
|
1151
142
|
? new InvitationService({
|
|
1152
143
|
invitationRepository: dependencies.invitationRepository,
|
|
1153
144
|
accountRepository: dependencies.accountRepository,
|
|
@@ -1160,8 +151,8 @@ export function createCore(dependencies) {
|
|
|
1160
151
|
// Accepting an invitation grants membership ⇒ drop the workspace-access cache (workspace-rbac).
|
|
1161
152
|
onAccountMembershipChanged: () => caches.workspaceAccess.invalidateAll(),
|
|
1162
153
|
})
|
|
1163
|
-
: undefined;
|
|
1164
|
-
|
|
154
|
+
: undefined);
|
|
155
|
+
modules.build('passwordReset', () => dependencies.passwordResetTokenRepository
|
|
1165
156
|
? new PasswordResetService({
|
|
1166
157
|
passwordResetTokenRepository: dependencies.passwordResetTokenRepository,
|
|
1167
158
|
userRepository: dependencies.userRepository,
|
|
@@ -1172,7 +163,7 @@ export function createCore(dependencies) {
|
|
|
1172
163
|
appBaseUrl: dependencies.appBaseUrl,
|
|
1173
164
|
logger: dependencies.logger,
|
|
1174
165
|
})
|
|
1175
|
-
: undefined;
|
|
166
|
+
: undefined);
|
|
1176
167
|
const pipelineService = new PipelineService(dependencies);
|
|
1177
168
|
const spendService = new SpendService({
|
|
1178
169
|
tokenUsageRepository: dependencies.tokenUsageRepository,
|
|
@@ -1191,7 +182,7 @@ export function createCore(dependencies) {
|
|
|
1191
182
|
userBudgetLimitCache: caches.userBudgetLimit,
|
|
1192
183
|
});
|
|
1193
184
|
spendServiceRef = spendService;
|
|
1194
|
-
|
|
185
|
+
modules.build('userSettings', () => dependencies.userSettingsRepository
|
|
1195
186
|
? {
|
|
1196
187
|
service: new UserSettingsService({
|
|
1197
188
|
userSettingsRepository: dependencies.userSettingsRepository,
|
|
@@ -1200,8 +191,8 @@ export function createCore(dependencies) {
|
|
|
1200
191
|
resolveUserBudgetCap: () => spendService.budgetCaps().userMonthlyLimitMax,
|
|
1201
192
|
}),
|
|
1202
193
|
}
|
|
1203
|
-
: undefined;
|
|
1204
|
-
const llmObservability = dependencies.llmCallMetricRepository
|
|
194
|
+
: undefined);
|
|
195
|
+
const llmObservability = modules.build('llmObservability', () => dependencies.llmCallMetricRepository
|
|
1205
196
|
? new LlmObservabilityService({
|
|
1206
197
|
llmCallMetricRepository: dependencies.llmCallMetricRepository,
|
|
1207
198
|
idGenerator: dependencies.idGenerator,
|
|
@@ -1211,13 +202,13 @@ export function createCore(dependencies) {
|
|
|
1211
202
|
workspaceSettingsRepository: dependencies.workspaceSettingsRepository,
|
|
1212
203
|
workspaceSettingsCache: caches.workspaceSettings,
|
|
1213
204
|
})
|
|
1214
|
-
: undefined;
|
|
1215
|
-
|
|
205
|
+
: undefined);
|
|
206
|
+
modules.build('platformObservability', () => dependencies.platformMetricsRepository
|
|
1216
207
|
? new PlatformObservabilityService({
|
|
1217
208
|
platformMetricsRepository: dependencies.platformMetricsRepository,
|
|
1218
209
|
clock: dependencies.clock,
|
|
1219
210
|
})
|
|
1220
|
-
: undefined;
|
|
211
|
+
: undefined);
|
|
1221
212
|
// The provisioning event log lives in a separate high-churn store. When its
|
|
1222
213
|
// repository is wired, build a best-effort recorder (threaded into the env
|
|
1223
214
|
// services) + the read service (exposed for the logs controller). The container
|
|
@@ -1229,28 +220,30 @@ export function createCore(dependencies) {
|
|
|
1229
220
|
clock: dependencies.clock,
|
|
1230
221
|
})
|
|
1231
222
|
: undefined;
|
|
1232
|
-
|
|
223
|
+
modules.build('provisioningLogs', () => dependencies.provisioningLogRepository
|
|
1233
224
|
? {
|
|
1234
|
-
service: new ProvisioningLogService({
|
|
225
|
+
service: new ProvisioningLogService({
|
|
226
|
+
repository: dependencies.provisioningLogRepository,
|
|
227
|
+
}),
|
|
1235
228
|
}
|
|
1236
|
-
: undefined;
|
|
229
|
+
: undefined);
|
|
1237
230
|
// Built before the shared-stacks + environments modules so a compose stack recipe's
|
|
1238
231
|
// `prerequisites` (and a shared stack's own prerequisites) are re-run at provision / bring-up
|
|
1239
232
|
// start through this service. The host probes exist only on the local facade; absent ⇒ a recipe /
|
|
1240
233
|
// stack that declares prerequisites fails loudly (the preflight API 503s too).
|
|
1241
|
-
const preflight = createPreflightModule(dependencies);
|
|
234
|
+
const preflight = modules.build('preflight', () => createPreflightModule(dependencies));
|
|
1242
235
|
// Built before the environments module so a compose stack recipe's `sharedStackRefs` can be
|
|
1243
236
|
// brought up (provider-before-consumer) through this service during provisioning. Persistence is
|
|
1244
237
|
// runtime-symmetric (present on every facade); the lifecycle only runs where a host daemon is
|
|
1245
238
|
// wired (`composeRuntime` — the local facade), else `ensureRefsUp` returns a clean error. It gets
|
|
1246
239
|
// the preflight service so a shared stack re-checks its own machine prerequisites at bring-up.
|
|
1247
|
-
const sharedStacks = createSharedStacksModule(dependencies, preflight?.service);
|
|
1248
|
-
const environments = createEnvironmentsModule(dependencies, provisioningLogRecorder, executionEventPublisher, sharedStacks?.service, preflight?.service);
|
|
240
|
+
const sharedStacks = modules.build('sharedStacks', () => createSharedStacksModule(dependencies, preflight?.service));
|
|
241
|
+
const environments = modules.build('environments', () => createEnvironmentsModule(dependencies, provisioningLogRecorder, executionEventPublisher, sharedStacks?.service, preflight?.service));
|
|
1249
242
|
// Built before the fragment library so a document-backed fragment can re-resolve
|
|
1250
243
|
// its linked Confluence/Notion/GitHub page through the document module's reader.
|
|
1251
|
-
const documents = createDocumentsModule(dependencies, boardService);
|
|
1252
|
-
const fragmentLibrary = createFragmentLibraryModule(dependencies, documents?.contentResolver, caches);
|
|
1253
|
-
const skillLibrary = createSkillLibraryModule(dependencies, caches);
|
|
244
|
+
const documents = modules.build('documents', () => createDocumentsModule(dependencies, boardService));
|
|
245
|
+
const fragmentLibrary = modules.build('fragmentLibrary', () => createFragmentLibraryModule(dependencies, documents?.contentResolver, caches));
|
|
246
|
+
const skillLibrary = modules.build('skillLibrary', () => createSkillLibraryModule(dependencies, caches));
|
|
1254
247
|
// Reconciles a `blueprints` step's decomposition onto the board. Needs only the
|
|
1255
248
|
// board service + block repository (both always present), so it is wired
|
|
1256
249
|
// unconditionally — there is no standalone scan command or persisted blueprint store.
|
|
@@ -1260,19 +253,19 @@ export function createCore(dependencies) {
|
|
|
1260
253
|
});
|
|
1261
254
|
// Built before the execution engine so it can raise merge-review / CI-failed /
|
|
1262
255
|
// pipeline-complete notifications during a run (when the module is configured).
|
|
1263
|
-
const notifications = createNotificationsModule(dependencies);
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
256
|
+
const notifications = modules.build('notifications', () => createNotificationsModule(dependencies));
|
|
257
|
+
modules.build('slack', () => createSlackModule(dependencies));
|
|
258
|
+
modules.build('riskPolicies', () => createRiskPoliciesModule(dependencies, caches));
|
|
259
|
+
modules.build('sandbox', () => createSandboxModule(dependencies, agentKindRegistry));
|
|
1267
260
|
// Built before the execution engine so the per-service running-task limit can be
|
|
1268
261
|
// enforced at start() (and the escalation sweep can read the waiting threshold).
|
|
1269
|
-
const settings = createWorkspaceSettingsModule(dependencies, caches.workspaceSettings);
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
262
|
+
const settings = modules.build('settings', () => createWorkspaceSettingsModule(dependencies, caches.workspaceSettings));
|
|
263
|
+
modules.build('releaseHealth', () => createReleaseHealthModule(dependencies));
|
|
264
|
+
modules.build('packageRegistries', () => createPackageRegistriesModule(dependencies));
|
|
265
|
+
modules.build('preview', () => createPreviewModule(dependencies));
|
|
266
|
+
modules.build('incidentEnrichmentSettings', () => createIncidentEnrichmentModule(dependencies));
|
|
267
|
+
modules.build('modelPresets', () => createModelPresetsModule(dependencies));
|
|
268
|
+
modules.build('serviceFragmentDefaults', () => createServiceFragmentDefaultsModule(dependencies));
|
|
1276
269
|
// Built before the execution engine so the planning pipeline's plan ingest + the
|
|
1277
270
|
// committer step's tracker mirror can run through it.
|
|
1278
271
|
const initiativeService = dependencies.initiativeRepository
|
|
@@ -1308,14 +301,14 @@ export function createCore(dependencies) {
|
|
|
1308
301
|
});
|
|
1309
302
|
// Built before the execution engine so the special `requirements-review` gate step can
|
|
1310
303
|
// drive the inline reviewer + the iterative answer → incorporate → re-review loop.
|
|
1311
|
-
const requirements = createRequirementsModule(dependencies, notifications?.service, fragmentLibrary);
|
|
304
|
+
const requirements = modules.build('requirements', () => createRequirementsModule(dependencies, notifications?.service, fragmentLibrary));
|
|
1312
305
|
const docInterview = createDocInterviewService(dependencies);
|
|
1313
306
|
const forkChat = createForkChatService(dependencies);
|
|
1314
|
-
const clarity = createClarityModule(dependencies, notifications?.service);
|
|
1315
|
-
const brainstorm = createBrainstormModule(dependencies, notifications?.service);
|
|
307
|
+
const clarity = modules.build('clarity', () => createClarityModule(dependencies, notifications?.service));
|
|
308
|
+
const brainstorm = modules.build('brainstorm', () => createBrainstormModule(dependencies, notifications?.service));
|
|
1316
309
|
// Built before the execution engine so the engine's terminal hook can schedule a
|
|
1317
310
|
// post-run Kaizen grading for each completed agent step.
|
|
1318
|
-
const kaizen = createKaizenModule(dependencies);
|
|
311
|
+
const kaizen = modules.build('kaizen', () => createKaizenModule(dependencies));
|
|
1319
312
|
// Late-bound so the engine's terminal hooks can poke the execution loop, which is built AFTER
|
|
1320
313
|
// the engine (the loop depends on `executionService.start`). Fire-and-forget; a null ref (the
|
|
1321
314
|
// loop unwired, or the settled block not part of an initiative) is a no-op.
|
|
@@ -1326,7 +319,7 @@ export function createCore(dependencies) {
|
|
|
1326
319
|
// Built before the execution engine so the engine's `bug-intake` step can drive the
|
|
1327
320
|
// read-and-claim intake helper (`tasks.bugIntakeService`). Also feeds the recurring module's
|
|
1328
321
|
// schedule intake-config validation below.
|
|
1329
|
-
const tasks = createTasksModule(dependencies, boardService);
|
|
322
|
+
const tasks = modules.build('tasks', () => createTasksModule(dependencies, boardService));
|
|
1330
323
|
const executionService = new ExecutionService({
|
|
1331
324
|
...dependencies,
|
|
1332
325
|
agentKindRegistry,
|
|
@@ -1394,13 +387,21 @@ export function createCore(dependencies) {
|
|
|
1394
387
|
? (workspaceId, agentKind, modelPresetId) => resolvePresetModelForKind(dependencies.modelPresetRepository, workspaceId, agentKind, modelPresetId)
|
|
1395
388
|
: undefined,
|
|
1396
389
|
});
|
|
1397
|
-
|
|
1398
|
-
|
|
390
|
+
modules.build('github', () => createGitHubModule(dependencies, caches));
|
|
391
|
+
modules.build('runners', () => createRunnersModule(dependencies));
|
|
1399
392
|
// After a bootstrap succeeds, map the new repo into a blueprint + the board by
|
|
1400
393
|
// starting the blueprint-only pipeline against the service frame.
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
394
|
+
modules.build('bootstrap', () => createBootstrapModule(dependencies, executionEventPublisher, (ws, blockId) => executionService.start(ws, blockId, BLUEPRINT_PIPELINE_ID).then(() => undefined)));
|
|
395
|
+
modules.build('tracker', () => createTrackerModule(dependencies));
|
|
396
|
+
modules.build('recurring', () => createRecurringModule(dependencies, executionService, executionEventPublisher, tasks?.connectionService));
|
|
397
|
+
// The env-config-repair module is a sub-module of `environments` — surfaced as its own top-level
|
|
398
|
+
// `Core.envConfigRepair` key. Registered here (not in the `environments` build above) so it emits
|
|
399
|
+
// through the same registry rather than a bespoke return spread.
|
|
400
|
+
modules.build('envConfigRepair', () => environments?.envConfigRepair);
|
|
401
|
+
// Observability + per-account settings that a facade builds and passes through on the deps bag.
|
|
402
|
+
modules.build('agentContextObservability', () => dependencies.agentContextObservability);
|
|
403
|
+
modules.build('searchQueryObservability', () => dependencies.searchQueryObservability);
|
|
404
|
+
modules.build('accountSettings', () => dependencies.accountSettings ? { service: dependencies.accountSettings } : undefined);
|
|
1404
405
|
// The initiative EXECUTION LOOP (slice 3): built after the engine (it drives
|
|
1405
406
|
// `executionService.start` to spawn tasks), then late-bound into the terminal poke above so a
|
|
1406
407
|
// settling child run advances its owning initiative immediately. Present only when initiatives
|
|
@@ -1421,19 +422,17 @@ export function createCore(dependencies) {
|
|
|
1421
422
|
})
|
|
1422
423
|
: undefined;
|
|
1423
424
|
initiativeLoopRef = initiativeLoop;
|
|
1424
|
-
|
|
425
|
+
modules.build('initiatives', () => initiativeService && initiativeLoop
|
|
1425
426
|
? { service: initiativeService, loop: initiativeLoop }
|
|
1426
|
-
: undefined;
|
|
1427
|
-
|
|
427
|
+
: undefined);
|
|
428
|
+
modules.build('services', () => createServicesModule(dependencies));
|
|
429
|
+
// The always-present spine, plus every optional module the registry assembled in ONE place
|
|
430
|
+
// (unwired keys absent) — replacing the ~40 hand-written `...(x ? { x } : {})` return spreads.
|
|
1428
431
|
return {
|
|
1429
432
|
caches,
|
|
1430
433
|
workspaceService,
|
|
1431
|
-
workspaceMemberService,
|
|
1432
434
|
accountService,
|
|
1433
435
|
userService,
|
|
1434
|
-
...(invitations ? { invitations } : {}),
|
|
1435
|
-
...(passwordReset ? { passwordReset } : {}),
|
|
1436
|
-
...(email ? { email } : {}),
|
|
1437
436
|
boardService,
|
|
1438
437
|
pipelineService,
|
|
1439
438
|
executionService,
|
|
@@ -1442,49 +441,7 @@ export function createCore(dependencies) {
|
|
|
1442
441
|
gateRegistry,
|
|
1443
442
|
initiativePresetRegistry,
|
|
1444
443
|
executionEventPublisher,
|
|
1445
|
-
...(
|
|
1446
|
-
...(platformObservability ? { platformObservability } : {}),
|
|
1447
|
-
...(dependencies.agentContextObservability
|
|
1448
|
-
? { agentContextObservability: dependencies.agentContextObservability }
|
|
1449
|
-
: {}),
|
|
1450
|
-
...(dependencies.searchQueryObservability
|
|
1451
|
-
? { searchQueryObservability: dependencies.searchQueryObservability }
|
|
1452
|
-
: {}),
|
|
1453
|
-
...(github ? { github } : {}),
|
|
1454
|
-
...(documents ? { documents } : {}),
|
|
1455
|
-
...(tasks ? { tasks } : {}),
|
|
1456
|
-
...(environments ? { environments } : {}),
|
|
1457
|
-
...(environments?.envConfigRepair ? { envConfigRepair: environments.envConfigRepair } : {}),
|
|
1458
|
-
...(runners ? { runners } : {}),
|
|
1459
|
-
...(provisioningLogs ? { provisioningLogs } : {}),
|
|
1460
|
-
...(bootstrap ? { bootstrap } : {}),
|
|
1461
|
-
...(requirements ? { requirements } : {}),
|
|
1462
|
-
...(kaizen ? { kaizen } : {}),
|
|
1463
|
-
...(clarity ? { clarity } : {}),
|
|
1464
|
-
...(brainstorm ? { brainstorm } : {}),
|
|
1465
|
-
...(notifications ? { notifications } : {}),
|
|
1466
|
-
...(slack ? { slack } : {}),
|
|
1467
|
-
...(riskPolicies ? { riskPolicies } : {}),
|
|
1468
|
-
...(sharedStacks ? { sharedStacks } : {}),
|
|
1469
|
-
...(preflight ? { preflight } : {}),
|
|
1470
|
-
...(sandbox ? { sandbox } : {}),
|
|
1471
|
-
...(settings ? { settings } : {}),
|
|
1472
|
-
...(userSettings ? { userSettings } : {}),
|
|
1473
|
-
...(releaseHealth ? { releaseHealth } : {}),
|
|
1474
|
-
...(packageRegistries ? { packageRegistries } : {}),
|
|
1475
|
-
...(preview ? { preview } : {}),
|
|
1476
|
-
...(incidentEnrichmentSettings ? { incidentEnrichmentSettings } : {}),
|
|
1477
|
-
...(dependencies.accountSettings
|
|
1478
|
-
? { accountSettings: { service: dependencies.accountSettings } }
|
|
1479
|
-
: {}),
|
|
1480
|
-
...(modelPresets ? { modelPresets } : {}),
|
|
1481
|
-
...(serviceFragmentDefaults ? { serviceFragmentDefaults } : {}),
|
|
1482
|
-
...(fragmentLibrary ? { fragmentLibrary } : {}),
|
|
1483
|
-
...(skillLibrary ? { skillLibrary } : {}),
|
|
1484
|
-
...(initiatives ? { initiatives } : {}),
|
|
1485
|
-
...(recurring ? { recurring } : {}),
|
|
1486
|
-
...(tracker ? { tracker } : {}),
|
|
1487
|
-
...(services ? { services } : {}),
|
|
444
|
+
...modules.assemble(),
|
|
1488
445
|
};
|
|
1489
446
|
}
|
|
1490
447
|
//# sourceMappingURL=container.js.map
|