@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.
Files changed (126) hide show
  1. package/README.md +104 -7
  2. package/REFERENCE.md +194 -19
  3. package/dist/agent/agent-admission.d.ts +80 -0
  4. package/dist/agent/agent-admission.d.ts.map +1 -0
  5. package/dist/agent/agent-admission.js +127 -0
  6. package/dist/agent/agent-admission.js.map +1 -0
  7. package/dist/agent/agent-ai-binding.d.ts +24 -0
  8. package/dist/agent/agent-ai-binding.d.ts.map +1 -0
  9. package/dist/agent/agent-ai-binding.js +48 -0
  10. package/dist/agent/agent-ai-binding.js.map +1 -0
  11. package/dist/agent/agent-ai-runtime.d.ts +76 -0
  12. package/dist/agent/agent-ai-runtime.d.ts.map +1 -0
  13. package/dist/agent/agent-ai-runtime.js +1269 -0
  14. package/dist/agent/agent-ai-runtime.js.map +1 -0
  15. package/dist/agent/agent-execution-capacity.d.ts +43 -0
  16. package/dist/agent/agent-execution-capacity.d.ts.map +1 -0
  17. package/dist/agent/agent-execution-capacity.js +147 -0
  18. package/dist/agent/agent-execution-capacity.js.map +1 -0
  19. package/dist/agent/agent-execution-fault.d.ts +18 -0
  20. package/dist/agent/agent-execution-fault.d.ts.map +1 -0
  21. package/dist/agent/agent-execution-fault.js +31 -0
  22. package/dist/agent/agent-execution-fault.js.map +1 -0
  23. package/dist/agent/agent-execution-session.d.ts +103 -0
  24. package/dist/agent/agent-execution-session.d.ts.map +1 -0
  25. package/dist/agent/agent-execution-session.js +238 -0
  26. package/dist/agent/agent-execution-session.js.map +1 -0
  27. package/dist/agent/agent-history.d.ts +148 -0
  28. package/dist/agent/agent-history.d.ts.map +1 -0
  29. package/dist/agent/agent-history.js +340 -0
  30. package/dist/agent/agent-history.js.map +1 -0
  31. package/dist/agent/agent-interaction-audit.d.ts +58 -0
  32. package/dist/agent/agent-interaction-audit.d.ts.map +1 -0
  33. package/dist/agent/agent-interaction-audit.js +114 -0
  34. package/dist/agent/agent-interaction-audit.js.map +1 -0
  35. package/dist/agent/agent-interaction-events.d.ts +40 -0
  36. package/dist/agent/agent-interaction-events.d.ts.map +1 -0
  37. package/dist/agent/agent-interaction-events.js +129 -0
  38. package/dist/agent/agent-interaction-events.js.map +1 -0
  39. package/dist/agent/agent-mcp-budget.d.ts +125 -0
  40. package/dist/agent/agent-mcp-budget.d.ts.map +1 -0
  41. package/dist/agent/agent-mcp-budget.js +566 -0
  42. package/dist/agent/agent-mcp-budget.js.map +1 -0
  43. package/dist/agent/agent-mcp-host.d.ts +125 -0
  44. package/dist/agent/agent-mcp-host.d.ts.map +1 -0
  45. package/dist/agent/agent-mcp-host.js +15 -0
  46. package/dist/agent/agent-mcp-host.js.map +1 -0
  47. package/dist/agent/agent-mcp-runtime.d.ts +199 -0
  48. package/dist/agent/agent-mcp-runtime.d.ts.map +1 -0
  49. package/dist/agent/agent-mcp-runtime.js +621 -0
  50. package/dist/agent/agent-mcp-runtime.js.map +1 -0
  51. package/dist/agent/agent-model-selection.d.ts +129 -0
  52. package/dist/agent/agent-model-selection.d.ts.map +1 -0
  53. package/dist/agent/agent-model-selection.js +342 -0
  54. package/dist/agent/agent-model-selection.js.map +1 -0
  55. package/dist/agent/agent-read-runtime.d.ts +70 -0
  56. package/dist/agent/agent-read-runtime.d.ts.map +1 -0
  57. package/dist/agent/agent-read-runtime.js +320 -0
  58. package/dist/agent/agent-read-runtime.js.map +1 -0
  59. package/dist/agent/agent-revisions.d.ts +35 -0
  60. package/dist/agent/agent-revisions.d.ts.map +1 -0
  61. package/dist/agent/agent-revisions.js +133 -0
  62. package/dist/agent/agent-revisions.js.map +1 -0
  63. package/dist/agent/agent-scheduler.d.ts +99 -0
  64. package/dist/agent/agent-scheduler.d.ts.map +1 -0
  65. package/dist/agent/agent-scheduler.js +398 -0
  66. package/dist/agent/agent-scheduler.js.map +1 -0
  67. package/dist/bus/command-bus.d.ts +62 -0
  68. package/dist/bus/command-bus.d.ts.map +1 -1
  69. package/dist/bus/command-bus.js +51 -2
  70. package/dist/bus/command-bus.js.map +1 -1
  71. package/dist/bus/event-bus.d.ts +137 -3
  72. package/dist/bus/event-bus.d.ts.map +1 -1
  73. package/dist/bus/event-bus.js +133 -2
  74. package/dist/bus/event-bus.js.map +1 -1
  75. package/dist/bus/saved-dispatcher-binding.d.ts +96 -0
  76. package/dist/bus/saved-dispatcher-binding.d.ts.map +1 -0
  77. package/dist/bus/saved-dispatcher-binding.js +35 -0
  78. package/dist/bus/saved-dispatcher-binding.js.map +1 -0
  79. package/dist/context/bounded-context.d.ts +36 -2
  80. package/dist/context/bounded-context.d.ts.map +1 -1
  81. package/dist/context/bounded-context.js +253 -54
  82. package/dist/context/bounded-context.js.map +1 -1
  83. package/dist/context/tenant-index.d.ts +25 -0
  84. package/dist/context/tenant-index.d.ts.map +1 -1
  85. package/dist/context/tenant-index.js +45 -1
  86. package/dist/context/tenant-index.js.map +1 -1
  87. package/dist/entity/entity.d.ts +82 -3
  88. package/dist/entity/entity.d.ts.map +1 -1
  89. package/dist/entity/entity.js +121 -22
  90. package/dist/entity/entity.js.map +1 -1
  91. package/dist/handler/generated-handler-registry.js +8 -3
  92. package/dist/handler/generated-handler-registry.js.map +1 -1
  93. package/dist/handler/handler-metadata.d.ts +17 -1
  94. package/dist/handler/handler-metadata.d.ts.map +1 -1
  95. package/dist/handler/handler-metadata.js +10 -4
  96. package/dist/handler/handler-metadata.js.map +1 -1
  97. package/dist/index.d.ts +2 -2
  98. package/dist/index.d.ts.map +1 -1
  99. package/dist/index.js +1 -1
  100. package/dist/index.js.map +1 -1
  101. package/dist/repository/commit-fence.d.ts +11 -0
  102. package/dist/repository/commit-fence.d.ts.map +1 -1
  103. package/dist/repository/commit-fence.js +13 -0
  104. package/dist/repository/commit-fence.js.map +1 -1
  105. package/dist/repository/repository.d.ts +222 -13
  106. package/dist/repository/repository.d.ts.map +1 -1
  107. package/dist/repository/repository.js +2323 -123
  108. package/dist/repository/repository.js.map +1 -1
  109. package/dist/runtime/signal-publisher.d.ts +6 -0
  110. package/dist/runtime/signal-publisher.d.ts.map +1 -1
  111. package/dist/runtime/signal-publisher.js +13 -0
  112. package/dist/runtime/signal-publisher.js.map +1 -1
  113. package/dist/runtime/standalone-handler-runtime.d.ts +7 -0
  114. package/dist/runtime/standalone-handler-runtime.d.ts.map +1 -1
  115. package/dist/runtime/standalone-handler-runtime.js +148 -11
  116. package/dist/runtime/standalone-handler-runtime.js.map +1 -1
  117. package/dist/server/server.d.ts +9 -0
  118. package/dist/server/server.d.ts.map +1 -1
  119. package/dist/server/server.js +13 -1
  120. package/dist/server/server.js.map +1 -1
  121. package/dist/testing/index.d.ts +57 -1
  122. package/dist/testing/index.d.ts.map +1 -1
  123. package/dist/testing/index.js +87 -0
  124. package/dist/testing/index.js.map +1 -1
  125. package/dist/tsconfig.tsbuildinfo +1 -1
  126. 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
- const preparedRepositories = [];
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
- return this.#registeredRepositories.map((snapshot) => ContextParts.createRepositoryView(snapshot));
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, buses.commandBus, buses.eventBus, buses.systemEventBus, buses.publisher, running.stand, buses.systemStand, running.runtime, buses.systemSpec, storageFactory, repositories, this.#deliveryStrategy);
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, commandBus, eventBus, systemEventBus, publisher, stand, systemStand, runtime, systemSpec, storageFactory, repositories, deliveryStrategy) {
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((schema) => schema.typeName.startsWith("spine.system."));
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
- repositoryAccess.bindRuntime(repository, {
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
  *