@spine-event-engine/server 2.0.0-snapshot.22 → 2.0.0-snapshot.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +104 -7
- package/REFERENCE.md +194 -19
- package/dist/agent/agent-admission.d.ts +80 -0
- package/dist/agent/agent-admission.d.ts.map +1 -0
- package/dist/agent/agent-admission.js +127 -0
- package/dist/agent/agent-admission.js.map +1 -0
- package/dist/agent/agent-ai-binding.d.ts +24 -0
- package/dist/agent/agent-ai-binding.d.ts.map +1 -0
- package/dist/agent/agent-ai-binding.js +48 -0
- package/dist/agent/agent-ai-binding.js.map +1 -0
- package/dist/agent/agent-ai-runtime.d.ts +76 -0
- package/dist/agent/agent-ai-runtime.d.ts.map +1 -0
- package/dist/agent/agent-ai-runtime.js +1269 -0
- package/dist/agent/agent-ai-runtime.js.map +1 -0
- package/dist/agent/agent-execution-capacity.d.ts +43 -0
- package/dist/agent/agent-execution-capacity.d.ts.map +1 -0
- package/dist/agent/agent-execution-capacity.js +147 -0
- package/dist/agent/agent-execution-capacity.js.map +1 -0
- package/dist/agent/agent-execution-fault.d.ts +18 -0
- package/dist/agent/agent-execution-fault.d.ts.map +1 -0
- package/dist/agent/agent-execution-fault.js +31 -0
- package/dist/agent/agent-execution-fault.js.map +1 -0
- package/dist/agent/agent-execution-session.d.ts +103 -0
- package/dist/agent/agent-execution-session.d.ts.map +1 -0
- package/dist/agent/agent-execution-session.js +238 -0
- package/dist/agent/agent-execution-session.js.map +1 -0
- package/dist/agent/agent-history.d.ts +148 -0
- package/dist/agent/agent-history.d.ts.map +1 -0
- package/dist/agent/agent-history.js +340 -0
- package/dist/agent/agent-history.js.map +1 -0
- package/dist/agent/agent-interaction-audit.d.ts +58 -0
- package/dist/agent/agent-interaction-audit.d.ts.map +1 -0
- package/dist/agent/agent-interaction-audit.js +114 -0
- package/dist/agent/agent-interaction-audit.js.map +1 -0
- package/dist/agent/agent-interaction-events.d.ts +40 -0
- package/dist/agent/agent-interaction-events.d.ts.map +1 -0
- package/dist/agent/agent-interaction-events.js +129 -0
- package/dist/agent/agent-interaction-events.js.map +1 -0
- package/dist/agent/agent-mcp-budget.d.ts +125 -0
- package/dist/agent/agent-mcp-budget.d.ts.map +1 -0
- package/dist/agent/agent-mcp-budget.js +566 -0
- package/dist/agent/agent-mcp-budget.js.map +1 -0
- package/dist/agent/agent-mcp-host.d.ts +125 -0
- package/dist/agent/agent-mcp-host.d.ts.map +1 -0
- package/dist/agent/agent-mcp-host.js +15 -0
- package/dist/agent/agent-mcp-host.js.map +1 -0
- package/dist/agent/agent-mcp-runtime.d.ts +199 -0
- package/dist/agent/agent-mcp-runtime.d.ts.map +1 -0
- package/dist/agent/agent-mcp-runtime.js +621 -0
- package/dist/agent/agent-mcp-runtime.js.map +1 -0
- package/dist/agent/agent-model-selection.d.ts +129 -0
- package/dist/agent/agent-model-selection.d.ts.map +1 -0
- package/dist/agent/agent-model-selection.js +342 -0
- package/dist/agent/agent-model-selection.js.map +1 -0
- package/dist/agent/agent-read-runtime.d.ts +70 -0
- package/dist/agent/agent-read-runtime.d.ts.map +1 -0
- package/dist/agent/agent-read-runtime.js +320 -0
- package/dist/agent/agent-read-runtime.js.map +1 -0
- package/dist/agent/agent-revisions.d.ts +35 -0
- package/dist/agent/agent-revisions.d.ts.map +1 -0
- package/dist/agent/agent-revisions.js +133 -0
- package/dist/agent/agent-revisions.js.map +1 -0
- package/dist/agent/agent-scheduler.d.ts +99 -0
- package/dist/agent/agent-scheduler.d.ts.map +1 -0
- package/dist/agent/agent-scheduler.js +398 -0
- package/dist/agent/agent-scheduler.js.map +1 -0
- package/dist/bus/command-bus.d.ts +62 -0
- package/dist/bus/command-bus.d.ts.map +1 -1
- package/dist/bus/command-bus.js +51 -2
- package/dist/bus/command-bus.js.map +1 -1
- package/dist/bus/event-bus.d.ts +137 -3
- package/dist/bus/event-bus.d.ts.map +1 -1
- package/dist/bus/event-bus.js +133 -2
- package/dist/bus/event-bus.js.map +1 -1
- package/dist/bus/saved-dispatcher-binding.d.ts +96 -0
- package/dist/bus/saved-dispatcher-binding.d.ts.map +1 -0
- package/dist/bus/saved-dispatcher-binding.js +35 -0
- package/dist/bus/saved-dispatcher-binding.js.map +1 -0
- package/dist/context/bounded-context.d.ts +36 -2
- package/dist/context/bounded-context.d.ts.map +1 -1
- package/dist/context/bounded-context.js +253 -54
- package/dist/context/bounded-context.js.map +1 -1
- package/dist/context/tenant-index.d.ts +25 -0
- package/dist/context/tenant-index.d.ts.map +1 -1
- package/dist/context/tenant-index.js +45 -1
- package/dist/context/tenant-index.js.map +1 -1
- package/dist/entity/entity.d.ts +82 -3
- package/dist/entity/entity.d.ts.map +1 -1
- package/dist/entity/entity.js +121 -22
- package/dist/entity/entity.js.map +1 -1
- package/dist/handler/generated-handler-registry.js +8 -3
- package/dist/handler/generated-handler-registry.js.map +1 -1
- package/dist/handler/handler-metadata.d.ts +17 -1
- package/dist/handler/handler-metadata.d.ts.map +1 -1
- package/dist/handler/handler-metadata.js +10 -4
- package/dist/handler/handler-metadata.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/repository/commit-fence.d.ts +11 -0
- package/dist/repository/commit-fence.d.ts.map +1 -1
- package/dist/repository/commit-fence.js +13 -0
- package/dist/repository/commit-fence.js.map +1 -1
- package/dist/repository/repository.d.ts +222 -13
- package/dist/repository/repository.d.ts.map +1 -1
- package/dist/repository/repository.js +2323 -123
- package/dist/repository/repository.js.map +1 -1
- package/dist/runtime/signal-publisher.d.ts +6 -0
- package/dist/runtime/signal-publisher.d.ts.map +1 -1
- package/dist/runtime/signal-publisher.js +13 -0
- package/dist/runtime/signal-publisher.js.map +1 -1
- package/dist/runtime/standalone-handler-runtime.d.ts +7 -0
- package/dist/runtime/standalone-handler-runtime.d.ts.map +1 -1
- package/dist/runtime/standalone-handler-runtime.js +148 -11
- package/dist/runtime/standalone-handler-runtime.js.map +1 -1
- package/dist/server/server.d.ts +9 -0
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/server.js +13 -1
- package/dist/server/server.js.map +1 -1
- package/dist/testing/index.d.ts +57 -1
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +87 -0
- package/dist/testing/index.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +14 -12
|
@@ -16,17 +16,21 @@ import { access, realpath } from "node:fs/promises";
|
|
|
16
16
|
import { isAbsolute, relative, resolve, sep } from "node:path";
|
|
17
17
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
18
18
|
import { clone, create, getOption, hasOption } from "@bufbuild/protobuf";
|
|
19
|
+
import { freezeRegistry, isAiModel, registryOptions } from "@spine-event-engine/ai/spi/runtime";
|
|
19
20
|
import { TypeUrls } from "@spine-event-engine/core";
|
|
20
21
|
import { EventSchema, EventContextSchema, BoundedContextNameSchema, TenantIdSchema, } from "@spine-event-engine/proto";
|
|
21
22
|
import { SPI_type, internal_all, internal_type } from "@spine-event-engine/proto";
|
|
22
23
|
import { ColumnTypes, EventStore, InMemoryStorageFactory, RecordColumn, } from "@spine-event-engine/storage";
|
|
23
|
-
import { TenantBoundary } from "@spine-event-engine/storage/provider";
|
|
24
|
+
import { AgentExecutionStorageFactories, AgentHistoryStorageFactories, TenantBoundary, } from "@spine-event-engine/storage/provider";
|
|
24
25
|
import { CommandBus, commandBusAccess } from "../bus/command-bus.js";
|
|
25
|
-
import { EventBus, eventBusAccess, } from "../bus/event-bus.js";
|
|
26
|
+
import { EventBus, eventBusAccess, isSystemEventSchema, } from "../bus/event-bus.js";
|
|
26
27
|
import { DeliveryReadiness, } from "./local-inbox-handoff.js";
|
|
27
28
|
import { LocalEntityInbox } from "./entity-inbox.js";
|
|
28
29
|
import { LocalProjectionInbox } from "./projection-handoff.js";
|
|
29
30
|
import { TenantIndexes } from "./tenant-index.js";
|
|
31
|
+
import { EffectiveTenants } from "./effective-tenant.js";
|
|
32
|
+
import { AgentScheduler } from "../agent/agent-scheduler.js";
|
|
33
|
+
import { AgentExecutionCapacity } from "../agent/agent-execution-capacity.js";
|
|
30
34
|
import { Repository, repositoryAccess, } from "../repository/repository.js";
|
|
31
35
|
import { GeneratedRegistryDiscovery } from "../handler/generated-registry-discovery.js";
|
|
32
36
|
import { HandlerRegistryIngestor, } from "../handler/generated-handler-registry.js";
|
|
@@ -81,6 +85,8 @@ const contextSubscriptionRuntimes = new WeakMap();
|
|
|
81
85
|
const contextLoggers = new WeakMap();
|
|
82
86
|
const contextSignalPublishers = new WeakMap();
|
|
83
87
|
const contextEventBuses = new WeakMap();
|
|
88
|
+
const contextRepositoryViews = new WeakMap();
|
|
89
|
+
const issuedRepositoryViews = new WeakMap();
|
|
84
90
|
const closingContexts = new WeakSet();
|
|
85
91
|
const contextClosePhases = new WeakMap();
|
|
86
92
|
const contextIntegrations = new WeakMap();
|
|
@@ -106,9 +112,12 @@ export class BoundedContext {
|
|
|
106
112
|
#registeredRepositories = [];
|
|
107
113
|
#repositoryViews = new Set();
|
|
108
114
|
#storageFactory;
|
|
115
|
+
#ai;
|
|
109
116
|
#stand;
|
|
110
117
|
#systemStand;
|
|
111
118
|
#subscriptionRuntime;
|
|
119
|
+
#agentScheduler;
|
|
120
|
+
#agentErrors = [];
|
|
112
121
|
#closed;
|
|
113
122
|
/**
|
|
114
123
|
* Registers the framework-only construction hook for this module.
|
|
@@ -132,6 +141,7 @@ export class BoundedContext {
|
|
|
132
141
|
this.#systemStand = input.systemStand;
|
|
133
142
|
this.#subscriptionRuntime = input.subscriptionRuntime;
|
|
134
143
|
this.#storageFactory = input.storageFactory;
|
|
144
|
+
this.#ai = input.ai;
|
|
135
145
|
this.#deliveryStrategy = ContextParts.snapshotDeliveryStrategy(input.deliveryStrategy);
|
|
136
146
|
this.#commandEndpoint = Object.freeze({
|
|
137
147
|
acceptedCommandTypes: () => this.#commandBus.acceptedCommandTypes(),
|
|
@@ -146,8 +156,56 @@ export class BoundedContext {
|
|
|
146
156
|
this.#projectionInbox = delivery.projectionInbox;
|
|
147
157
|
this.#installReferences(input, delivery);
|
|
148
158
|
this.#registerAndStart(input.repositories, delivery.tenantIndex);
|
|
159
|
+
this.#startAgentScheduler(delivery.tenantIndex);
|
|
149
160
|
Object.freeze(this);
|
|
150
161
|
}
|
|
162
|
+
/**
|
|
163
|
+
* Starts bounded indexed discovery for registered Agent repositories.
|
|
164
|
+
*/
|
|
165
|
+
#startAgentScheduler(tenantIndex) {
|
|
166
|
+
if (this.#ai === undefined)
|
|
167
|
+
return;
|
|
168
|
+
const repositories = [...this.#repositoryViews].filter((entry) => entry.entityFamily === "agent");
|
|
169
|
+
if (repositories.length === 0)
|
|
170
|
+
return;
|
|
171
|
+
this.#agentScheduler = new AgentScheduler({
|
|
172
|
+
repositories: repositories.length,
|
|
173
|
+
page: (after, signal) => tenantIndex.page({
|
|
174
|
+
count: 16,
|
|
175
|
+
signal,
|
|
176
|
+
...(after === undefined ? {} : { after }),
|
|
177
|
+
}),
|
|
178
|
+
scope: (id, index) => {
|
|
179
|
+
const repository = repositories[index];
|
|
180
|
+
if (repository === undefined)
|
|
181
|
+
throw new Error("Agent repository scope is unavailable.");
|
|
182
|
+
return this.#agentScope(repository, tenantIndex.tenantMode === "single-tenant" ? undefined : id);
|
|
183
|
+
},
|
|
184
|
+
}, AgentExecutionCapacity.for(this.#ai), (error) => {
|
|
185
|
+
if (this.#agentErrors.length < 32)
|
|
186
|
+
this.#agentErrors.push(error);
|
|
187
|
+
});
|
|
188
|
+
this.#agentScheduler.start();
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Builds one scan scope from an accepted handoff or a catalog sweep.
|
|
192
|
+
*
|
|
193
|
+
* @param repository Registered Agent repository.
|
|
194
|
+
* @param tenantId Accepted tenant, absent only in a single-tenant Bounded Context.
|
|
195
|
+
* @returns The matching indexed provider scan scope.
|
|
196
|
+
*/
|
|
197
|
+
#agentScope(repository, tenantId) {
|
|
198
|
+
const effective = EffectiveTenants.current(this.#snapshot.tenantMode === "multitenant", tenantId);
|
|
199
|
+
return {
|
|
200
|
+
id: JSON.stringify([
|
|
201
|
+
this.#snapshot.name.value,
|
|
202
|
+
String(TenantBoundary.from(effective).key),
|
|
203
|
+
repository.stateFullTypeName,
|
|
204
|
+
]),
|
|
205
|
+
pending: (after, count) => repositoryAccess.pendingAcceptedAgents(repository, tenantId, after, count),
|
|
206
|
+
run: (key, signal) => repositoryAccess.runAcceptedAgent(repository, tenantId, key, signal),
|
|
207
|
+
};
|
|
208
|
+
}
|
|
151
209
|
/**
|
|
152
210
|
* Creates tenant-aware Inboxes and their shared readiness signal.
|
|
153
211
|
*
|
|
@@ -174,6 +232,7 @@ export class BoundedContext {
|
|
|
174
232
|
*/
|
|
175
233
|
#installReferences(input, delivery) {
|
|
176
234
|
contextEventBuses.set(this, [this.#eventBus, this.#systemEventBus]);
|
|
235
|
+
contextRepositoryViews.set(this, this.#repositoryViews);
|
|
177
236
|
eventSubscribers.set(this, (typeUrl, subscriber) => eventBusAccess.subscribe(this.#eventBus, typeUrl, subscriber));
|
|
178
237
|
systemEventPosters.set(this, (event) => this.#publisher.publishSystemEvent(event));
|
|
179
238
|
contextSystemPairings.set(this, ContextParts.createSystemPairing(this.#snapshot, input.systemSpec));
|
|
@@ -236,10 +295,27 @@ export class BoundedContext {
|
|
|
236
295
|
}
|
|
237
296
|
}
|
|
238
297
|
#prepareRepositories(repositories) {
|
|
298
|
+
const registration = this.#repositoryRegistration();
|
|
299
|
+
const preparedRepositories = [];
|
|
300
|
+
try {
|
|
301
|
+
for (const repository of repositories) {
|
|
302
|
+
preparedRepositories.push(ContextParts.prepareRepositoryForContext(repository, registration));
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
catch (error) {
|
|
306
|
+
this.#failRegistration(error, preparedRepositories);
|
|
307
|
+
}
|
|
308
|
+
return preparedRepositories;
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Builds callbacks shared by every repository prepared for this Bounded Context.
|
|
312
|
+
*/
|
|
313
|
+
#repositoryRegistration() {
|
|
239
314
|
const registration = {
|
|
240
315
|
name: ContextParts.cloneName(this.#snapshot.name),
|
|
241
316
|
storageContext: ContextParts.createStorageMode(this.#snapshot.spec),
|
|
242
317
|
storageFactory: this.#storageFactory,
|
|
318
|
+
...(this.#ai === undefined ? {} : { ai: this.#ai }),
|
|
243
319
|
stand: this.#stand,
|
|
244
320
|
entityInbox: this.#entityInbox,
|
|
245
321
|
projectionInbox: this.#projectionInbox,
|
|
@@ -250,17 +326,19 @@ export class BoundedContext {
|
|
|
250
326
|
registerSystemEventSchema: (schema) => {
|
|
251
327
|
eventBusAccess.registerSchemas(this.#systemEventBus, [schema]);
|
|
252
328
|
},
|
|
329
|
+
prepareSavedEvent: (event) => eventBusAccess.prepareSaved(this.#eventBus, event),
|
|
330
|
+
prepareSavedCommand: (command) => commandBusAccess.prepareSaved(this.#commandBus, command),
|
|
331
|
+
publishSavedEvent: (event, plan) => eventBusAccess.postSavedFollowUp(this.#eventBus, event, plan),
|
|
332
|
+
publishSavedCommand: (command, plan) => commandBusAccess.postSavedFollowUp(this.#commandBus, command, plan),
|
|
333
|
+
publishAgentSystemEvent: (event) => eventBusAccess.postFollowUp(this.#systemEventBus, event),
|
|
334
|
+
recordAcceptedSaved: (signal) => {
|
|
335
|
+
this.#publisher.recordAcceptedSaved(signal);
|
|
336
|
+
},
|
|
337
|
+
wakeAcceptedAgent: (repository, tenantId) => {
|
|
338
|
+
this.#agentScheduler?.wake(this.#agentScope(repository, tenantId));
|
|
339
|
+
},
|
|
253
340
|
};
|
|
254
|
-
|
|
255
|
-
try {
|
|
256
|
-
for (const repository of repositories) {
|
|
257
|
-
preparedRepositories.push(ContextParts.prepareRepositoryForContext(repository, registration));
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
catch (error) {
|
|
261
|
-
this.#failRegistration(error, preparedRepositories);
|
|
262
|
-
}
|
|
263
|
-
return preparedRepositories;
|
|
341
|
+
return registration;
|
|
264
342
|
}
|
|
265
343
|
#failRegistration(error, preparedRepositories) {
|
|
266
344
|
const closeErrors = ContextParts.closePreparedRepositories(preparedRepositories);
|
|
@@ -357,7 +435,33 @@ export class BoundedContext {
|
|
|
357
435
|
* @returns Returns immutable repository views.
|
|
358
436
|
*/
|
|
359
437
|
registeredRepositories() {
|
|
360
|
-
|
|
438
|
+
const originals = [...this.#repositoryViews];
|
|
439
|
+
return this.#registeredRepositories.map((snapshot, index) => {
|
|
440
|
+
const repository = originals[index];
|
|
441
|
+
if (repository === undefined)
|
|
442
|
+
throw new Error("Registered repository view disappeared.");
|
|
443
|
+
const view = ContextParts.createRepositoryView(snapshot);
|
|
444
|
+
issuedRepositoryViews.set(view, { context: this, repository });
|
|
445
|
+
return view;
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* Returns the repository already registered for an exact Entity constructor.
|
|
450
|
+
* This trusted server-side lookup does not load Entity state or open storage.
|
|
451
|
+
*
|
|
452
|
+
* @typeParam EntityType Concrete Entity constructor registered in this Bounded Context.
|
|
453
|
+
* @param entityType Exact Entity class supplied to this Bounded Context's builder.
|
|
454
|
+
* @returns The existing typed repository for that Entity class.
|
|
455
|
+
* @throws When the constructor is absent or the Bounded Context is closing.
|
|
456
|
+
*/
|
|
457
|
+
getRepository(entityType) {
|
|
458
|
+
if (closingContexts.has(this))
|
|
459
|
+
throw new Error("Bounded Context is closing.");
|
|
460
|
+
for (const repository of this.#repositoryViews) {
|
|
461
|
+
if (repository.entityType === entityType)
|
|
462
|
+
return repository;
|
|
463
|
+
}
|
|
464
|
+
throw new Error("Entity constructor is not registered in this Bounded Context.");
|
|
361
465
|
}
|
|
362
466
|
/**
|
|
363
467
|
* Clears and locally replays every registered Projection from already-stored events.
|
|
@@ -435,6 +539,7 @@ export class BoundedContext {
|
|
|
435
539
|
*/
|
|
436
540
|
#beginClose() {
|
|
437
541
|
closingContexts.add(this);
|
|
542
|
+
this.#agentScheduler?.stop();
|
|
438
543
|
this.#publisher.beginClose();
|
|
439
544
|
commandBusAccess.beginClose(this.#commandBus);
|
|
440
545
|
eventBusAccess.beginClose(this.#eventBus);
|
|
@@ -459,6 +564,7 @@ export class BoundedContext {
|
|
|
459
564
|
*/
|
|
460
565
|
async #closeOnce() {
|
|
461
566
|
const errors = [];
|
|
567
|
+
await ContextParts.closeContextPart(() => this.#agentScheduler?.close(), errors);
|
|
462
568
|
await ContextParts.closeContextPart(() => this.#drainWork(), errors);
|
|
463
569
|
this.#publisher.finishClose();
|
|
464
570
|
await ContextParts.closeContextPart(() => commandBusAccess.finishClose(this.#commandBus), errors);
|
|
@@ -498,6 +604,22 @@ export class BoundedContext {
|
|
|
498
604
|
* Exposes framework-only operations for built contexts and their builders.
|
|
499
605
|
*/
|
|
500
606
|
export const boundedContextAccess = Object.freeze({
|
|
607
|
+
/**
|
|
608
|
+
* Resolves a view only within the Bounded Context that issued it.
|
|
609
|
+
*
|
|
610
|
+
* @param context Built Bounded Context that registered the repository.
|
|
611
|
+
* @param view Candidate original or issued view.
|
|
612
|
+
* @returns Registered repository, or undefined for a foreign view.
|
|
613
|
+
*/
|
|
614
|
+
resolveRepository(context, view) {
|
|
615
|
+
const originals = contextRepositoryViews.get(context);
|
|
616
|
+
if (originals?.has(view))
|
|
617
|
+
return view;
|
|
618
|
+
const issued = issuedRepositoryViews.get(view);
|
|
619
|
+
return issued?.context === context && originals?.has(issued.repository)
|
|
620
|
+
? issued.repository
|
|
621
|
+
: undefined;
|
|
622
|
+
},
|
|
501
623
|
beginClose(context) {
|
|
502
624
|
const phase = contextClosePhases.get(context);
|
|
503
625
|
if (phase === undefined)
|
|
@@ -543,12 +665,12 @@ export const boundedContextAccess = Object.freeze({
|
|
|
543
665
|
value !== null &&
|
|
544
666
|
builderBuilds.has(value));
|
|
545
667
|
},
|
|
546
|
-
build(builder, defaultStorageFactory) {
|
|
668
|
+
build(builder, defaultStorageFactory, defaultAi) {
|
|
547
669
|
const build = builderBuilds.get(builder);
|
|
548
670
|
if (build === undefined) {
|
|
549
671
|
throw new TypeError("Builder access requires a BoundedContextBuilder instance.");
|
|
550
672
|
}
|
|
551
|
-
return build(defaultStorageFactory);
|
|
673
|
+
return build(defaultStorageFactory, defaultAi);
|
|
552
674
|
},
|
|
553
675
|
subscribeToEvent(context, typeUrl, subscriber) {
|
|
554
676
|
const subscribe = eventSubscribers.get(context);
|
|
@@ -627,6 +749,7 @@ export class BoundedContextBuilder {
|
|
|
627
749
|
#generatedRepositoryOptions = new Map();
|
|
628
750
|
#deliveryStrategy = UniformAcrossAllShards.singleShard();
|
|
629
751
|
#storageFactory;
|
|
752
|
+
#aiRegistry;
|
|
630
753
|
#subscriptionRegistry;
|
|
631
754
|
#persistSystemEvents = false;
|
|
632
755
|
#generatedRegistryRoot;
|
|
@@ -645,7 +768,7 @@ export class BoundedContextBuilder {
|
|
|
645
768
|
constructor(specSnapshot, token) {
|
|
646
769
|
ContextParts.requireFrameworkConstructionToken(token, "BoundedContextBuilder instances are framework-owned.");
|
|
647
770
|
this.#specSnapshot = ContextParts.cloneSpecSnapshot(specSnapshot);
|
|
648
|
-
builderBuilds.set(this, (defaultStorageFactory) => this.#buildAsyncWith(defaultStorageFactory));
|
|
771
|
+
builderBuilds.set(this, (defaultStorageFactory, defaultAi) => this.#buildAsyncWith(defaultStorageFactory, defaultAi));
|
|
649
772
|
Object.freeze(this);
|
|
650
773
|
}
|
|
651
774
|
/**
|
|
@@ -722,6 +845,7 @@ export class BoundedContextBuilder {
|
|
|
722
845
|
* A standalone commander requires generated receiver metadata and therefore
|
|
723
846
|
* this builder's `buildAsync()` path. It installs its Command and Event sides
|
|
724
847
|
* exactly once.
|
|
848
|
+
*
|
|
725
849
|
* @returns This builder for further configuration.
|
|
726
850
|
*/
|
|
727
851
|
addCommandDispatcher(dispatcher) {
|
|
@@ -747,6 +871,7 @@ export class BoundedContextBuilder {
|
|
|
747
871
|
*
|
|
748
872
|
* @param dispatcher Raw event dispatcher, or a standalone reactor/subscriber.
|
|
749
873
|
* Standalone receivers require generated receiver metadata and `buildAsync()`.
|
|
874
|
+
*
|
|
750
875
|
* @returns This builder for further configuration.
|
|
751
876
|
*/
|
|
752
877
|
addEventDispatcher(dispatcher) {
|
|
@@ -791,6 +916,16 @@ export class BoundedContextBuilder {
|
|
|
791
916
|
this.#storageFactory = storageFactory;
|
|
792
917
|
return this;
|
|
793
918
|
}
|
|
919
|
+
/**
|
|
920
|
+
* Sets the application AI registry before this Bounded Context is built.
|
|
921
|
+
*
|
|
922
|
+
* @param registry Factory-created deployment registry.
|
|
923
|
+
* @returns This builder.
|
|
924
|
+
*/
|
|
925
|
+
withAi(registry) {
|
|
926
|
+
this.#aiRegistry = registry;
|
|
927
|
+
return this;
|
|
928
|
+
}
|
|
794
929
|
/**
|
|
795
930
|
* Persists internal system events in the paired System Context storage.
|
|
796
931
|
*
|
|
@@ -846,7 +981,7 @@ export class BoundedContextBuilder {
|
|
|
846
981
|
throw new Error("Standalone generated handlers require buildAsync().");
|
|
847
982
|
}
|
|
848
983
|
ContextParts.rejectSyncEntityAssembly(this.#entityTypes);
|
|
849
|
-
return this.#buildWith([...this.#repositories], this.#storageFactory ?? new InMemoryStorageFactory());
|
|
984
|
+
return this.#buildWith([...this.#repositories], this.#storageFactory ?? new InMemoryStorageFactory(), [], this.#aiRegistry);
|
|
850
985
|
}
|
|
851
986
|
/**
|
|
852
987
|
* Builds a context after loading generated metadata for added entity classes
|
|
@@ -861,10 +996,10 @@ export class BoundedContextBuilder {
|
|
|
861
996
|
async buildAsync() {
|
|
862
997
|
return this.#buildAsyncWith();
|
|
863
998
|
}
|
|
864
|
-
async #buildAsyncWith(defaultStorageFactory) {
|
|
999
|
+
async #buildAsyncWith(defaultStorageFactory, defaultAi) {
|
|
865
1000
|
const generated = await this.#loadGeneratedArtifacts([...this.#entityTypes]);
|
|
866
1001
|
const repositories = [...this.#repositories, ...generated.repositories];
|
|
867
|
-
const context = this.#buildWith(repositories, this.#storageFactory ?? defaultStorageFactory ?? new InMemoryStorageFactory(), generated.standalone);
|
|
1002
|
+
const context = this.#buildWith(repositories, this.#storageFactory ?? defaultStorageFactory ?? new InMemoryStorageFactory(), generated.standalone, this.#aiRegistry ?? defaultAi);
|
|
868
1003
|
try {
|
|
869
1004
|
await ContextParts.integrationReady(context);
|
|
870
1005
|
}
|
|
@@ -879,19 +1014,22 @@ export class BoundedContextBuilder {
|
|
|
879
1014
|
}
|
|
880
1015
|
return context;
|
|
881
1016
|
}
|
|
882
|
-
#buildWith(repositories, storageFactory, standalone = []) {
|
|
1017
|
+
#buildWith(repositories, storageFactory, standalone = [], ai) {
|
|
883
1018
|
const resources = this.#subscriptionRegistry === undefined ? {} : { registry: this.#subscriptionRegistry };
|
|
884
1019
|
this.#subscriptionRegistry = undefined;
|
|
885
1020
|
const registeredRepositories = [...repositories];
|
|
886
1021
|
try {
|
|
887
|
-
return this.#assembleContext(registeredRepositories, storageFactory, standalone, resources);
|
|
1022
|
+
return this.#assembleContext(registeredRepositories, storageFactory, standalone, resources, ai);
|
|
888
1023
|
}
|
|
889
1024
|
catch (error) {
|
|
890
1025
|
return this.#failBuild(resources, error);
|
|
891
1026
|
}
|
|
892
1027
|
}
|
|
893
|
-
#assembleContext(repositories, storageFactory, standalone, resources) {
|
|
1028
|
+
#assembleContext(repositories, storageFactory, standalone, resources, ai) {
|
|
894
1029
|
ContextParts.preflightRepositories(repositories);
|
|
1030
|
+
this.#preflightAgents(repositories, storageFactory, ai);
|
|
1031
|
+
if (ai !== undefined)
|
|
1032
|
+
freezeRegistry(ai);
|
|
895
1033
|
const dispatchers = this.#buildDispatchers(repositories);
|
|
896
1034
|
const buses = this.#buildBuses(repositories, storageFactory, dispatchers, resources);
|
|
897
1035
|
const standaloneEvent = this.#installStandalone(standalone, buses);
|
|
@@ -902,13 +1040,61 @@ export class BoundedContextBuilder {
|
|
|
902
1040
|
...ContextParts.standaloneProducedEventSchemas(standalone),
|
|
903
1041
|
]);
|
|
904
1042
|
const running = this.#buildRuntime(storageFactory, buses, resources);
|
|
905
|
-
const context = this.#createContext(repositories, storageFactory, buses, running);
|
|
1043
|
+
const context = this.#createContext(repositories, storageFactory, buses, running, ai);
|
|
906
1044
|
ContextParts.attachIntegration(context, buses.eventBus, buses.systemSpec, ContextParts.externalEventSchemas([
|
|
907
1045
|
...dispatchers.domain,
|
|
908
1046
|
...(standaloneEvent === undefined ? [] : [standaloneEvent]),
|
|
909
1047
|
]));
|
|
910
1048
|
return context;
|
|
911
1049
|
}
|
|
1050
|
+
/**
|
|
1051
|
+
* Rejects missing Agent prerequisites before repository intake is installed.
|
|
1052
|
+
*
|
|
1053
|
+
* @param repositories Repositories selected for this Bounded Context.
|
|
1054
|
+
* @param storageFactory Provider supplying mandatory Agent records.
|
|
1055
|
+
* @param ai Effective Bounded Context or server registry.
|
|
1056
|
+
*/
|
|
1057
|
+
#preflightAgents(repositories, storageFactory, ai) {
|
|
1058
|
+
const agents = repositories.filter((repository) => repository.entityFamily === "agent");
|
|
1059
|
+
if (agents.length === 0)
|
|
1060
|
+
return;
|
|
1061
|
+
if (ai === undefined)
|
|
1062
|
+
throw new TypeError("Agent registration requires an AI registry.");
|
|
1063
|
+
registryOptions(ai);
|
|
1064
|
+
if (!this.#persistSystemEvents)
|
|
1065
|
+
throw new TypeError("Agent registration requires persisted System Events.");
|
|
1066
|
+
if (!AgentHistoryStorageFactories.supports(storageFactory))
|
|
1067
|
+
throw new TypeError("Agent registration requires indexed history storage.");
|
|
1068
|
+
if (!AgentExecutionStorageFactories.supports(storageFactory))
|
|
1069
|
+
throw new TypeError("Agent registration requires durable execution storage.");
|
|
1070
|
+
this.#checkAgentOutputBindings(agents);
|
|
1071
|
+
for (const repository of agents) {
|
|
1072
|
+
const config = repositoryAccess.agentConfiguration(repository);
|
|
1073
|
+
if (config.codeRevision === undefined || config.codeRevision.trim() === "")
|
|
1074
|
+
throw new TypeError("Agent registration requires a code revision.");
|
|
1075
|
+
if (config.ai === undefined)
|
|
1076
|
+
throw new TypeError("Agent registration requires repository capabilities.");
|
|
1077
|
+
if (config.ai.models.some((model) => !isAiModel(model)))
|
|
1078
|
+
throw new TypeError("Agent repository capabilities must be factory-created models.");
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
/**
|
|
1082
|
+
* Rejects Agent outcomes whose raw matching dispatcher cannot bind a saved plan.
|
|
1083
|
+
*/
|
|
1084
|
+
#checkAgentOutputBindings(agents) {
|
|
1085
|
+
const rawEvents = new Set([...this.#eventDispatchers].flatMap((dispatcher) => dispatcher.messageSchemas().map((schema) => TypeUrls.derive(schema))));
|
|
1086
|
+
const rawCommands = new Set([...this.#commandDispatchers].flatMap((dispatcher) => dispatcher.messageSchemas().map((schema) => TypeUrls.derive(schema))));
|
|
1087
|
+
for (const repository of agents) {
|
|
1088
|
+
if (repositoryAccess
|
|
1089
|
+
.producedEventSchemas(repository)
|
|
1090
|
+
.some((schema) => rawEvents.has(TypeUrls.derive(schema))))
|
|
1091
|
+
throw new TypeError("Agent Event output requires a durable dispatcher binding.");
|
|
1092
|
+
if (repositoryAccess
|
|
1093
|
+
.producedCommandSchemas(repository)
|
|
1094
|
+
.some((schema) => rawCommands.has(TypeUrls.derive(schema))))
|
|
1095
|
+
throw new TypeError("Agent Command output requires a durable dispatcher binding.");
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
912
1098
|
#buildDispatchers(repositories) {
|
|
913
1099
|
const event = [
|
|
914
1100
|
...ContextParts.repositoryEventDispatchers(repositories),
|
|
@@ -998,8 +1184,8 @@ export class BoundedContextBuilder {
|
|
|
998
1184
|
resources.runtime = runtime;
|
|
999
1185
|
return { stand, registry, runtime };
|
|
1000
1186
|
}
|
|
1001
|
-
#createContext(repositories, storageFactory, buses, running) {
|
|
1002
|
-
return ContextParts.createBoundedContext(this.#specSnapshot,
|
|
1187
|
+
#createContext(repositories, storageFactory, buses, running, ai) {
|
|
1188
|
+
return ContextParts.createBoundedContext(this.#specSnapshot, storageFactory, repositories, this.#deliveryStrategy, buses, running, ai);
|
|
1003
1189
|
}
|
|
1004
1190
|
#failBuild(resources, error) {
|
|
1005
1191
|
const cleanupErrors = [];
|
|
@@ -1271,37 +1457,33 @@ const ContextParts = Object.freeze({
|
|
|
1271
1457
|
* Creates a bounded context from assembled runtime components.
|
|
1272
1458
|
*
|
|
1273
1459
|
* @param specSnapshot Context configuration.
|
|
1274
|
-
* @param commandBus Command dispatch bus.
|
|
1275
|
-
* @param eventBus Domain event bus.
|
|
1276
|
-
* @param systemEventBus System event bus.
|
|
1277
|
-
* @param publisher Signal publisher.
|
|
1278
|
-
* @param stand Domain read-side Stand.
|
|
1279
|
-
* @param systemStand System read-side Stand.
|
|
1280
|
-
* @param runtime Subscription runtime.
|
|
1281
|
-
* @param systemSpec Paired System specification.
|
|
1282
1460
|
* @param storageFactory Storage provider.
|
|
1283
1461
|
* @param repositories Registered repository views.
|
|
1284
1462
|
* @param deliveryStrategy Delivery sharding strategy.
|
|
1463
|
+
* @param buses Prepared domain and System buses.
|
|
1464
|
+
* @param running Prepared Stand and subscription runtime.
|
|
1465
|
+
* @param ai Effective AI registry when Agent repositories are present.
|
|
1285
1466
|
* @returns Constructed bounded context.
|
|
1286
1467
|
*/
|
|
1287
|
-
createBoundedContext(specSnapshot,
|
|
1468
|
+
createBoundedContext(specSnapshot, storageFactory, repositories, deliveryStrategy, buses, running, ai) {
|
|
1288
1469
|
return constructBoundedContext({
|
|
1289
1470
|
snapshot: {
|
|
1290
1471
|
name: specSnapshot.name,
|
|
1291
1472
|
tenantMode: ContextParts.toTenantMode(specSnapshot.multitenant),
|
|
1292
1473
|
spec: specSnapshot,
|
|
1293
1474
|
},
|
|
1294
|
-
commandBus,
|
|
1295
|
-
eventBus,
|
|
1296
|
-
systemEventBus,
|
|
1297
|
-
publisher,
|
|
1298
|
-
stand,
|
|
1299
|
-
systemStand,
|
|
1300
|
-
subscriptionRuntime: runtime,
|
|
1301
|
-
systemSpec,
|
|
1475
|
+
commandBus: buses.commandBus,
|
|
1476
|
+
eventBus: buses.eventBus,
|
|
1477
|
+
systemEventBus: buses.systemEventBus,
|
|
1478
|
+
publisher: buses.publisher,
|
|
1479
|
+
stand: running.stand,
|
|
1480
|
+
systemStand: buses.systemStand,
|
|
1481
|
+
subscriptionRuntime: running.runtime,
|
|
1482
|
+
systemSpec: buses.systemSpec,
|
|
1302
1483
|
storageFactory,
|
|
1303
1484
|
repositories,
|
|
1304
1485
|
deliveryStrategy,
|
|
1486
|
+
...(ai === undefined ? {} : { ai }),
|
|
1305
1487
|
token: frameworkConstructionToken,
|
|
1306
1488
|
});
|
|
1307
1489
|
},
|
|
@@ -2070,7 +2252,7 @@ const ContextParts = Object.freeze({
|
|
|
2070
2252
|
*/
|
|
2071
2253
|
isSystemEventDispatcher(dispatcher) {
|
|
2072
2254
|
const schemas = [...dispatcher.messageSchemas()];
|
|
2073
|
-
const systemSchemas = schemas.filter(
|
|
2255
|
+
const systemSchemas = schemas.filter(isSystemEventSchema);
|
|
2074
2256
|
if (systemSchemas.length > 0 && systemSchemas.length !== schemas.length) {
|
|
2075
2257
|
throw new Error("An EventDispatcher cannot mix domain and system event schemas.");
|
|
2076
2258
|
}
|
|
@@ -2272,17 +2454,7 @@ const ContextParts = Object.freeze({
|
|
|
2272
2454
|
ContextParts.requireRepositoryInstance(repository, "BoundedContext repository registration");
|
|
2273
2455
|
const snapshot = ContextParts.repositorySnapshot(repository);
|
|
2274
2456
|
ContextParts.rejectRegisteredRepository(repository);
|
|
2275
|
-
|
|
2276
|
-
context: registration.storageContext,
|
|
2277
|
-
storageFactory: registration.storageFactory,
|
|
2278
|
-
stand: registration.stand,
|
|
2279
|
-
signalMetadata: new SignalMetadata(),
|
|
2280
|
-
entityInbox: registration.entityInbox,
|
|
2281
|
-
projectionInbox: registration.projectionInbox,
|
|
2282
|
-
publisher: registration.publisher,
|
|
2283
|
-
registerEventSchema: registration.registerEventSchema,
|
|
2284
|
-
registerSystemEventSchema: registration.registerSystemEventSchema,
|
|
2285
|
-
});
|
|
2457
|
+
ContextParts.bindRepositoryRuntime(repository, registration);
|
|
2286
2458
|
const entityInboxTarget = repositoryAccess.entityInboxTarget(repository);
|
|
2287
2459
|
const projectionInboxTarget = repositoryAccess.projectionInboxTarget(repository);
|
|
2288
2460
|
return {
|
|
@@ -2298,6 +2470,33 @@ const ContextParts = Object.freeze({
|
|
|
2298
2470
|
},
|
|
2299
2471
|
};
|
|
2300
2472
|
},
|
|
2473
|
+
/**
|
|
2474
|
+
* Binds Bounded Context services to a prepared repository before target registration.
|
|
2475
|
+
*
|
|
2476
|
+
* @param repository Prepared repository receiving runtime services.
|
|
2477
|
+
* @param registration Bounded Context services and schemas.
|
|
2478
|
+
*/
|
|
2479
|
+
bindRepositoryRuntime(repository, registration) {
|
|
2480
|
+
repositoryAccess.bindRuntime(repository, {
|
|
2481
|
+
context: registration.storageContext,
|
|
2482
|
+
storageFactory: registration.storageFactory,
|
|
2483
|
+
...(registration.ai === undefined ? {} : { ai: registration.ai }),
|
|
2484
|
+
stand: registration.stand,
|
|
2485
|
+
signalMetadata: new SignalMetadata(),
|
|
2486
|
+
entityInbox: registration.entityInbox,
|
|
2487
|
+
projectionInbox: registration.projectionInbox,
|
|
2488
|
+
publisher: registration.publisher,
|
|
2489
|
+
registerEventSchema: registration.registerEventSchema,
|
|
2490
|
+
registerSystemEventSchema: registration.registerSystemEventSchema,
|
|
2491
|
+
prepareSavedEvent: registration.prepareSavedEvent,
|
|
2492
|
+
prepareSavedCommand: registration.prepareSavedCommand,
|
|
2493
|
+
publishSavedEvent: registration.publishSavedEvent,
|
|
2494
|
+
publishSavedCommand: registration.publishSavedCommand,
|
|
2495
|
+
publishAgentSystemEvent: registration.publishAgentSystemEvent,
|
|
2496
|
+
recordAcceptedSaved: registration.recordAcceptedSaved,
|
|
2497
|
+
wakeAcceptedAgent: registration.wakeAcceptedAgent,
|
|
2498
|
+
});
|
|
2499
|
+
},
|
|
2301
2500
|
/**
|
|
2302
2501
|
* Captures repository metadata needed for context registration.
|
|
2303
2502
|
*
|