@spine-event-engine/server 2.0.0-snapshot.22 → 2.0.0-snapshot.24

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 (199) hide show
  1. package/README.md +104 -7
  2. package/REFERENCE.md +272 -31
  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 +101 -2
  72. package/dist/bus/event-bus.d.ts.map +1 -1
  73. package/dist/bus/event-bus.js +74 -13
  74. package/dist/bus/event-bus.js.map +1 -1
  75. package/dist/bus/saved-dispatcher-binding.d.ts +53 -0
  76. package/dist/bus/saved-dispatcher-binding.d.ts.map +1 -0
  77. package/dist/bus/saved-dispatcher-binding.js +27 -0
  78. package/dist/bus/saved-dispatcher-binding.js.map +1 -0
  79. package/dist/context/bounded-context.d.ts +45 -12
  80. package/dist/context/bounded-context.d.ts.map +1 -1
  81. package/dist/context/bounded-context.js +306 -86
  82. package/dist/context/bounded-context.js.map +1 -1
  83. package/dist/context/entity-inbox.d.ts.map +1 -1
  84. package/dist/context/entity-inbox.js +21 -7
  85. package/dist/context/entity-inbox.js.map +1 -1
  86. package/dist/context/local-inbox-handoff.d.ts +1 -0
  87. package/dist/context/local-inbox-handoff.d.ts.map +1 -1
  88. package/dist/context/local-inbox-handoff.js +31 -1
  89. package/dist/context/local-inbox-handoff.js.map +1 -1
  90. package/dist/context/projection-handoff.d.ts.map +1 -1
  91. package/dist/context/projection-handoff.js +2 -0
  92. package/dist/context/projection-handoff.js.map +1 -1
  93. package/dist/context/tenant-index.d.ts +25 -0
  94. package/dist/context/tenant-index.d.ts.map +1 -1
  95. package/dist/context/tenant-index.js +45 -1
  96. package/dist/context/tenant-index.js.map +1 -1
  97. package/dist/delivery/delivery-monitor.d.ts +12 -9
  98. package/dist/delivery/delivery-monitor.d.ts.map +1 -1
  99. package/dist/delivery/delivery-monitor.js +15 -9
  100. package/dist/delivery/delivery-monitor.js.map +1 -1
  101. package/dist/delivery/delivery-ports.d.ts +6 -13
  102. package/dist/delivery/delivery-ports.d.ts.map +1 -1
  103. package/dist/delivery/delivery-run-control.d.ts +15 -0
  104. package/dist/delivery/delivery-run-control.d.ts.map +1 -1
  105. package/dist/delivery/delivery-run-control.js.map +1 -1
  106. package/dist/delivery/delivery-supervisor.d.ts +33 -5
  107. package/dist/delivery/delivery-supervisor.d.ts.map +1 -1
  108. package/dist/delivery/delivery-supervisor.js +150 -22
  109. package/dist/delivery/delivery-supervisor.js.map +1 -1
  110. package/dist/delivery/delivery.d.ts.map +1 -1
  111. package/dist/delivery/delivery.js +128 -54
  112. package/dist/delivery/delivery.js.map +1 -1
  113. package/dist/delivery/inbox-storage.d.ts +8 -11
  114. package/dist/delivery/inbox-storage.d.ts.map +1 -1
  115. package/dist/delivery/inbox-storage.js +33 -76
  116. package/dist/delivery/inbox-storage.js.map +1 -1
  117. package/dist/delivery/inbox.d.ts +9 -12
  118. package/dist/delivery/inbox.d.ts.map +1 -1
  119. package/dist/delivery/inbox.js +10 -13
  120. package/dist/delivery/inbox.js.map +1 -1
  121. package/dist/delivery/reception-observation.d.ts +56 -0
  122. package/dist/delivery/reception-observation.d.ts.map +1 -0
  123. package/dist/delivery/reception-observation.js +67 -0
  124. package/dist/delivery/reception-observation.js.map +1 -0
  125. package/dist/entity/entity.d.ts +82 -3
  126. package/dist/entity/entity.d.ts.map +1 -1
  127. package/dist/entity/entity.js +121 -22
  128. package/dist/entity/entity.js.map +1 -1
  129. package/dist/handler/generated-handler-registry.js +8 -3
  130. package/dist/handler/generated-handler-registry.js.map +1 -1
  131. package/dist/handler/handler-metadata.d.ts +17 -1
  132. package/dist/handler/handler-metadata.d.ts.map +1 -1
  133. package/dist/handler/handler-metadata.js +10 -4
  134. package/dist/handler/handler-metadata.js.map +1 -1
  135. package/dist/index.d.ts +2 -2
  136. package/dist/index.d.ts.map +1 -1
  137. package/dist/index.js +1 -1
  138. package/dist/index.js.map +1 -1
  139. package/dist/repository/commit-fence.d.ts +11 -0
  140. package/dist/repository/commit-fence.d.ts.map +1 -1
  141. package/dist/repository/commit-fence.js +13 -0
  142. package/dist/repository/commit-fence.js.map +1 -1
  143. package/dist/repository/repository.d.ts +221 -13
  144. package/dist/repository/repository.d.ts.map +1 -1
  145. package/dist/repository/repository.js +2323 -134
  146. package/dist/repository/repository.js.map +1 -1
  147. package/dist/runtime/runtime.d.ts +11 -0
  148. package/dist/runtime/runtime.d.ts.map +1 -1
  149. package/dist/runtime/runtime.js +13 -0
  150. package/dist/runtime/runtime.js.map +1 -1
  151. package/dist/runtime/signal-metadata.d.ts.map +1 -1
  152. package/dist/runtime/signal-metadata.js +3 -10
  153. package/dist/runtime/signal-metadata.js.map +1 -1
  154. package/dist/runtime/signal-publisher.d.ts +14 -30
  155. package/dist/runtime/signal-publisher.d.ts.map +1 -1
  156. package/dist/runtime/signal-publisher.js +75 -47
  157. package/dist/runtime/signal-publisher.js.map +1 -1
  158. package/dist/runtime/standalone-handler-runtime.d.ts +7 -0
  159. package/dist/runtime/standalone-handler-runtime.d.ts.map +1 -1
  160. package/dist/runtime/standalone-handler-runtime.js +118 -11
  161. package/dist/runtime/standalone-handler-runtime.js.map +1 -1
  162. package/dist/server/environment-delivery-worker.d.ts.map +1 -1
  163. package/dist/server/environment-delivery-worker.js +305 -5
  164. package/dist/server/environment-delivery-worker.js.map +1 -1
  165. package/dist/server/managed-server-application.d.ts +2 -2
  166. package/dist/server/managed-server-application.d.ts.map +1 -1
  167. package/dist/server/managed-server-application.js +467 -177
  168. package/dist/server/managed-server-application.js.map +1 -1
  169. package/dist/server/node-coordinator.d.ts +14 -0
  170. package/dist/server/node-coordinator.d.ts.map +1 -1
  171. package/dist/server/node-coordinator.js +266 -45
  172. package/dist/server/node-coordinator.js.map +1 -1
  173. package/dist/server/server-log.d.ts.map +1 -1
  174. package/dist/server/server-log.js +4 -0
  175. package/dist/server/server-log.js.map +1 -1
  176. package/dist/server/server.d.ts +9 -0
  177. package/dist/server/server.d.ts.map +1 -1
  178. package/dist/server/server.js +33 -8
  179. package/dist/server/server.js.map +1 -1
  180. package/dist/services/spine-services.d.ts.map +1 -1
  181. package/dist/services/spine-services.js +200 -72
  182. package/dist/services/spine-services.js.map +1 -1
  183. package/dist/stand/event-origin-filter.d.ts +16 -0
  184. package/dist/stand/event-origin-filter.d.ts.map +1 -0
  185. package/dist/stand/event-origin-filter.js +124 -0
  186. package/dist/stand/event-origin-filter.js.map +1 -0
  187. package/dist/stand/subscription-observer.d.ts.map +1 -1
  188. package/dist/stand/subscription-observer.js +7 -0
  189. package/dist/stand/subscription-observer.js.map +1 -1
  190. package/dist/stand/subscription-runtime.d.ts +18 -1
  191. package/dist/stand/subscription-runtime.d.ts.map +1 -1
  192. package/dist/stand/subscription-runtime.js +32 -4
  193. package/dist/stand/subscription-runtime.js.map +1 -1
  194. package/dist/testing/index.d.ts +57 -1
  195. package/dist/testing/index.d.ts.map +1 -1
  196. package/dist/testing/index.js +88 -1
  197. package/dist/testing/index.js.map +1 -1
  198. package/dist/tsconfig.tsbuildinfo +1 -1
  199. package/package.json +15 -13
@@ -16,17 +16,26 @@ 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
- import { ColumnTypes, EventStore, InMemoryStorageFactory, RecordColumn, } from "@spine-event-engine/storage";
23
- import { TenantBoundary } from "@spine-event-engine/storage/provider";
23
+ import { ColumnTypes, EventStore, InMemoryStorageFactory, RecordColumn, StorageGroup, } from "@spine-event-engine/storage";
24
+ import { AgentExecutionStorageFactories, AgentHistoryStorageFactories, TenantBoundary, } from "@spine-event-engine/storage/provider";
25
+ /**
26
+ * Storage family shared by paired System Event publication and trusted reads.
27
+ * @internal
28
+ */
29
+ export const systemEventGroup = new StorageGroup("spine_system_events");
24
30
  import { CommandBus, commandBusAccess } from "../bus/command-bus.js";
25
- import { EventBus, eventBusAccess, } from "../bus/event-bus.js";
31
+ import { EventBus, eventBusAccess, isSystemEventSchema, } from "../bus/event-bus.js";
26
32
  import { DeliveryReadiness, } from "./local-inbox-handoff.js";
27
33
  import { LocalEntityInbox } from "./entity-inbox.js";
28
34
  import { LocalProjectionInbox } from "./projection-handoff.js";
29
35
  import { TenantIndexes } from "./tenant-index.js";
36
+ import { EffectiveTenants } from "./effective-tenant.js";
37
+ import { AgentScheduler } from "../agent/agent-scheduler.js";
38
+ import { AgentExecutionCapacity } from "../agent/agent-execution-capacity.js";
30
39
  import { Repository, repositoryAccess, } from "../repository/repository.js";
31
40
  import { GeneratedRegistryDiscovery } from "../handler/generated-registry-discovery.js";
32
41
  import { HandlerRegistryIngestor, } from "../handler/generated-handler-registry.js";
@@ -81,6 +90,8 @@ const contextSubscriptionRuntimes = new WeakMap();
81
90
  const contextLoggers = new WeakMap();
82
91
  const contextSignalPublishers = new WeakMap();
83
92
  const contextEventBuses = new WeakMap();
93
+ const contextRepositoryViews = new WeakMap();
94
+ const issuedRepositoryViews = new WeakMap();
84
95
  const closingContexts = new WeakSet();
85
96
  const contextClosePhases = new WeakMap();
86
97
  const contextIntegrations = new WeakMap();
@@ -106,9 +117,12 @@ export class BoundedContext {
106
117
  #registeredRepositories = [];
107
118
  #repositoryViews = new Set();
108
119
  #storageFactory;
120
+ #ai;
109
121
  #stand;
110
122
  #systemStand;
111
123
  #subscriptionRuntime;
124
+ #agentScheduler;
125
+ #agentErrors = [];
112
126
  #closed;
113
127
  /**
114
128
  * Registers the framework-only construction hook for this module.
@@ -132,6 +146,7 @@ export class BoundedContext {
132
146
  this.#systemStand = input.systemStand;
133
147
  this.#subscriptionRuntime = input.subscriptionRuntime;
134
148
  this.#storageFactory = input.storageFactory;
149
+ this.#ai = input.ai;
135
150
  this.#deliveryStrategy = ContextParts.snapshotDeliveryStrategy(input.deliveryStrategy);
136
151
  this.#commandEndpoint = Object.freeze({
137
152
  acceptedCommandTypes: () => this.#commandBus.acceptedCommandTypes(),
@@ -146,8 +161,56 @@ export class BoundedContext {
146
161
  this.#projectionInbox = delivery.projectionInbox;
147
162
  this.#installReferences(input, delivery);
148
163
  this.#registerAndStart(input.repositories, delivery.tenantIndex);
164
+ this.#startAgentScheduler(delivery.tenantIndex);
149
165
  Object.freeze(this);
150
166
  }
167
+ /**
168
+ * Starts bounded indexed discovery for registered Agent repositories.
169
+ */
170
+ #startAgentScheduler(tenantIndex) {
171
+ if (this.#ai === undefined)
172
+ return;
173
+ const repositories = [...this.#repositoryViews].filter((entry) => entry.entityFamily === "agent");
174
+ if (repositories.length === 0)
175
+ return;
176
+ this.#agentScheduler = new AgentScheduler({
177
+ repositories: repositories.length,
178
+ page: (after, signal) => tenantIndex.page({
179
+ count: 16,
180
+ signal,
181
+ ...(after === undefined ? {} : { after }),
182
+ }),
183
+ scope: (id, index) => {
184
+ const repository = repositories[index];
185
+ if (repository === undefined)
186
+ throw new Error("Agent repository scope is unavailable.");
187
+ return this.#agentScope(repository, tenantIndex.tenantMode === "single-tenant" ? undefined : id);
188
+ },
189
+ }, AgentExecutionCapacity.for(this.#ai), (error) => {
190
+ if (this.#agentErrors.length < 32)
191
+ this.#agentErrors.push(error);
192
+ });
193
+ this.#agentScheduler.start();
194
+ }
195
+ /**
196
+ * Builds one scan scope from an accepted handoff or a catalog sweep.
197
+ *
198
+ * @param repository Registered Agent repository.
199
+ * @param tenantId Accepted tenant, absent only in a single-tenant Bounded Context.
200
+ * @returns The matching indexed provider scan scope.
201
+ */
202
+ #agentScope(repository, tenantId) {
203
+ const effective = EffectiveTenants.current(this.#snapshot.tenantMode === "multitenant", tenantId);
204
+ return {
205
+ id: JSON.stringify([
206
+ this.#snapshot.name.value,
207
+ String(TenantBoundary.from(effective).key),
208
+ repository.stateFullTypeName,
209
+ ]),
210
+ pending: (after, count) => repositoryAccess.pendingAcceptedAgents(repository, tenantId, after, count),
211
+ run: (key, signal) => repositoryAccess.runAcceptedAgent(repository, tenantId, key, signal),
212
+ };
213
+ }
151
214
  /**
152
215
  * Creates tenant-aware Inboxes and their shared readiness signal.
153
216
  *
@@ -174,6 +237,7 @@ export class BoundedContext {
174
237
  */
175
238
  #installReferences(input, delivery) {
176
239
  contextEventBuses.set(this, [this.#eventBus, this.#systemEventBus]);
240
+ contextRepositoryViews.set(this, this.#repositoryViews);
177
241
  eventSubscribers.set(this, (typeUrl, subscriber) => eventBusAccess.subscribe(this.#eventBus, typeUrl, subscriber));
178
242
  systemEventPosters.set(this, (event) => this.#publisher.publishSystemEvent(event));
179
243
  contextSystemPairings.set(this, ContextParts.createSystemPairing(this.#snapshot, input.systemSpec));
@@ -236,10 +300,27 @@ export class BoundedContext {
236
300
  }
237
301
  }
238
302
  #prepareRepositories(repositories) {
303
+ const registration = this.#repositoryRegistration();
304
+ const preparedRepositories = [];
305
+ try {
306
+ for (const repository of repositories) {
307
+ preparedRepositories.push(ContextParts.prepareRepositoryForContext(repository, registration));
308
+ }
309
+ }
310
+ catch (error) {
311
+ this.#failRegistration(error, preparedRepositories);
312
+ }
313
+ return preparedRepositories;
314
+ }
315
+ /**
316
+ * Builds callbacks shared by every repository prepared for this Bounded Context.
317
+ */
318
+ #repositoryRegistration() {
239
319
  const registration = {
240
320
  name: ContextParts.cloneName(this.#snapshot.name),
241
321
  storageContext: ContextParts.createStorageMode(this.#snapshot.spec),
242
322
  storageFactory: this.#storageFactory,
323
+ ...(this.#ai === undefined ? {} : { ai: this.#ai }),
243
324
  stand: this.#stand,
244
325
  entityInbox: this.#entityInbox,
245
326
  projectionInbox: this.#projectionInbox,
@@ -250,17 +331,20 @@ export class BoundedContext {
250
331
  registerSystemEventSchema: (schema) => {
251
332
  eventBusAccess.registerSchemas(this.#systemEventBus, [schema]);
252
333
  },
334
+ publishAgentEvent: (event) => event.context?.rejection === undefined
335
+ ? this.#publisher.publishEvent(event)
336
+ : this.#publisher.publishRejectionEvent(event),
337
+ prepareSavedCommand: (command) => commandBusAccess.prepareSaved(this.#commandBus, command),
338
+ publishSavedCommand: (command, plan) => commandBusAccess.postSavedFollowUp(this.#commandBus, command, plan),
339
+ publishAgentSystemEvent: (event) => this.#publisher.publishSystemEvent(event),
340
+ recordAcceptedSaved: (signal) => {
341
+ this.#publisher.recordAcceptedSaved(signal);
342
+ },
343
+ wakeAcceptedAgent: (repository, tenantId) => {
344
+ this.#agentScheduler?.wake(this.#agentScope(repository, tenantId));
345
+ },
253
346
  };
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;
347
+ return registration;
264
348
  }
265
349
  #failRegistration(error, preparedRepositories) {
266
350
  const closeErrors = ContextParts.closePreparedRepositories(preparedRepositories);
@@ -357,7 +441,33 @@ export class BoundedContext {
357
441
  * @returns Returns immutable repository views.
358
442
  */
359
443
  registeredRepositories() {
360
- return this.#registeredRepositories.map((snapshot) => ContextParts.createRepositoryView(snapshot));
444
+ const originals = [...this.#repositoryViews];
445
+ return this.#registeredRepositories.map((snapshot, index) => {
446
+ const repository = originals[index];
447
+ if (repository === undefined)
448
+ throw new Error("Registered repository view disappeared.");
449
+ const view = ContextParts.createRepositoryView(snapshot);
450
+ issuedRepositoryViews.set(view, { context: this, repository });
451
+ return view;
452
+ });
453
+ }
454
+ /**
455
+ * Returns the repository already registered for an exact Entity constructor.
456
+ * This trusted server-side lookup does not load Entity state or open storage.
457
+ *
458
+ * @typeParam EntityType Concrete Entity constructor registered in this Bounded Context.
459
+ * @param entityType Exact Entity class supplied to this Bounded Context's builder.
460
+ * @returns The existing typed repository for that Entity class.
461
+ * @throws When the constructor is absent or the Bounded Context is closing.
462
+ */
463
+ getRepository(entityType) {
464
+ if (closingContexts.has(this))
465
+ throw new Error("Bounded Context is closing.");
466
+ for (const repository of this.#repositoryViews) {
467
+ if (repository.entityType === entityType)
468
+ return repository;
469
+ }
470
+ throw new Error("Entity constructor is not registered in this Bounded Context.");
361
471
  }
362
472
  /**
363
473
  * Clears and locally replays every registered Projection from already-stored events.
@@ -374,7 +484,7 @@ export class BoundedContext {
374
484
  * - single-tenant contexts reject `tenantId`;
375
485
  * - multitenant contexts require the exact non-blank `tenantId`;
376
486
  * - process-local sequential execution on the same EventBus runtime queue as
377
- * live event intake and stored redispatch.
487
+ * live event intake.
378
488
  *
379
489
  * Unsupported boundary:
380
490
  * - Delivery jobs, schedulers, inbox lifecycle, retries, and transport
@@ -416,14 +526,8 @@ export class BoundedContext {
416
526
  });
417
527
  }
418
528
  /**
419
- * Closes this context's buses, Stand, and repository storage and runtime bindings.
420
- *
421
- * Close is idempotent and returns the same close outcome on repeated calls.
422
- * The context attempts every close hook; when any hook fails, the
423
- * returned promise rejects with an `AggregateError` after the remaining hooks
424
- * have also been attempted.
425
- *
426
- * @returns A promise that settles after all resources close.
529
+ * Closes this context's buses, Stand, repository storage, and runtime bindings.
530
+ * @returns Completion or an aggregate of close failures.
427
531
  */
428
532
  close() {
429
533
  this.#beginClose();
@@ -431,10 +535,11 @@ export class BoundedContext {
431
535
  return this.#closed;
432
536
  }
433
537
  /**
434
- * Stops admission to this context's signal and bus work.
538
+ * Stops external admission while accepted follow-ups drain.
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);
@@ -446,9 +551,10 @@ export class BoundedContext {
446
551
  * @returns Completion after the context's accepted work settles.
447
552
  */
448
553
  async #drainWork() {
554
+ this.#beginClose();
449
555
  const errors = [];
450
- await ContextParts.closeContextPart(() => ContextParts.closeIntegration(this), errors);
451
- await ContextParts.closeContextPart(() => ContextParts.drainContextWork(this.#commandBus, this.#eventBus, this.#systemEventBus, this.#publisher), errors);
556
+ await this.#closePart(() => ContextParts.closeIntegration(this), errors);
557
+ await this.#closePart(() => ContextParts.drainContextWork(this.#commandBus, this.#eventBus, this.#systemEventBus, this.#publisher), errors);
452
558
  if (errors.length > 0)
453
559
  throw new AggregateError(errors, "BoundedContext drain failed.");
454
560
  }
@@ -459,18 +565,13 @@ export class BoundedContext {
459
565
  */
460
566
  async #closeOnce() {
461
567
  const errors = [];
462
- await ContextParts.closeContextPart(() => this.#drainWork(), errors);
463
- this.#publisher.finishClose();
464
- await ContextParts.closeContextPart(() => commandBusAccess.finishClose(this.#commandBus), errors);
465
- await ContextParts.closeContextPart(() => eventBusAccess.finishClose(this.#eventBus), errors);
466
- this.#subscriptionRuntime.beginClose();
467
- await ContextParts.closeContextPart(() => eventBusAccess.drain(this.#systemEventBus), errors);
468
- await ContextParts.closeContextPart(() => this.#subscriptionRuntime.drainClose(), errors);
469
- await ContextParts.closeContextPart(() => eventBusAccess.finishClose(this.#systemEventBus), errors);
470
- await ContextParts.closeContextPart(() => this.#stand.close(), errors);
471
- await ContextParts.closeContextPart(() => this.#systemStand.close(), errors);
472
- await ContextParts.closeContextPart(() => this.#subscriptionRuntime.finishClose(), errors);
473
- await ContextParts.closeContextPart(() => {
568
+ await this.#closePart(() => this.#agentScheduler?.close(), errors);
569
+ await this.#closePart(() => this.#drainWork(), errors);
570
+ await this.#closeBuses(errors);
571
+ await this.#closePart(() => this.#stand.close(), errors);
572
+ await this.#closePart(() => this.#systemStand.close(), errors);
573
+ await this.#closePart(() => this.#subscriptionRuntime.finishClose(), errors);
574
+ await this.#closePart(() => {
474
575
  ContextParts.requireTenantIndex(this).close();
475
576
  }, errors);
476
577
  await this.#closeRepositories(errors);
@@ -480,6 +581,21 @@ export class BoundedContext {
480
581
  }
481
582
  ContextParts.clearContextMetadata(this);
482
583
  }
584
+ /**
585
+ * Closes bus intake and subscription drains before the Stands.
586
+ *
587
+ * @param errors Collects independent close failures.
588
+ * @returns Completion after bounded bus close attempts.
589
+ */
590
+ async #closeBuses(errors) {
591
+ this.#publisher.finishClose();
592
+ await this.#closePart(() => commandBusAccess.finishClose(this.#commandBus), errors);
593
+ await this.#closePart(() => eventBusAccess.finishClose(this.#eventBus), errors);
594
+ this.#subscriptionRuntime.beginClose();
595
+ await this.#closePart(() => eventBusAccess.drain(this.#systemEventBus), errors);
596
+ await this.#closePart(() => this.#subscriptionRuntime.drainClose(), errors);
597
+ await this.#closePart(() => eventBusAccess.finishClose(this.#systemEventBus), errors);
598
+ }
483
599
  /**
484
600
  * Clears repository runtime registration after Stand closure.
485
601
  *
@@ -487,17 +603,41 @@ export class BoundedContext {
487
603
  */
488
604
  async #closeRepositories(errors) {
489
605
  for (const repository of this.#repositoryViews) {
490
- await ContextParts.closeContextPart(() => {
606
+ await this.#closePart(() => {
491
607
  repositoryAccess.clearRuntime(repository);
492
608
  registeredRepositories.delete(repository);
493
609
  }, errors);
494
610
  }
495
611
  }
612
+ /**
613
+ * Calls a close action and collects its failure before later cleanup.
614
+ * @param work Close action.
615
+ * @param errors Collects close failures.
616
+ */
617
+ async #closePart(work, errors) {
618
+ await ContextParts.closeContextPart(work, errors);
619
+ }
496
620
  }
497
621
  /**
498
622
  * Exposes framework-only operations for built contexts and their builders.
499
623
  */
500
624
  export const boundedContextAccess = Object.freeze({
625
+ /**
626
+ * Resolves a view only within the Bounded Context that issued it.
627
+ *
628
+ * @param context Built Bounded Context that registered the repository.
629
+ * @param view Candidate original or issued view.
630
+ * @returns Registered repository, or undefined for a foreign view.
631
+ */
632
+ resolveRepository(context, view) {
633
+ const originals = contextRepositoryViews.get(context);
634
+ if (originals?.has(view))
635
+ return view;
636
+ const issued = issuedRepositoryViews.get(view);
637
+ return issued?.context === context && originals?.has(issued.repository)
638
+ ? issued.repository
639
+ : undefined;
640
+ },
501
641
  beginClose(context) {
502
642
  const phase = contextClosePhases.get(context);
503
643
  if (phase === undefined)
@@ -543,12 +683,12 @@ export const boundedContextAccess = Object.freeze({
543
683
  value !== null &&
544
684
  builderBuilds.has(value));
545
685
  },
546
- build(builder, defaultStorageFactory) {
686
+ build(builder, defaultStorageFactory, defaultAi) {
547
687
  const build = builderBuilds.get(builder);
548
688
  if (build === undefined) {
549
689
  throw new TypeError("Builder access requires a BoundedContextBuilder instance.");
550
690
  }
551
- return build(defaultStorageFactory);
691
+ return build(defaultStorageFactory, defaultAi);
552
692
  },
553
693
  subscribeToEvent(context, typeUrl, subscriber) {
554
694
  const subscribe = eventSubscribers.get(context);
@@ -627,6 +767,7 @@ export class BoundedContextBuilder {
627
767
  #generatedRepositoryOptions = new Map();
628
768
  #deliveryStrategy = UniformAcrossAllShards.singleShard();
629
769
  #storageFactory;
770
+ #aiRegistry;
630
771
  #subscriptionRegistry;
631
772
  #persistSystemEvents = false;
632
773
  #generatedRegistryRoot;
@@ -645,7 +786,7 @@ export class BoundedContextBuilder {
645
786
  constructor(specSnapshot, token) {
646
787
  ContextParts.requireFrameworkConstructionToken(token, "BoundedContextBuilder instances are framework-owned.");
647
788
  this.#specSnapshot = ContextParts.cloneSpecSnapshot(specSnapshot);
648
- builderBuilds.set(this, (defaultStorageFactory) => this.#buildAsyncWith(defaultStorageFactory));
789
+ builderBuilds.set(this, (defaultStorageFactory, defaultAi) => this.#buildAsyncWith(defaultStorageFactory, defaultAi));
649
790
  Object.freeze(this);
650
791
  }
651
792
  /**
@@ -722,6 +863,7 @@ export class BoundedContextBuilder {
722
863
  * A standalone commander requires generated receiver metadata and therefore
723
864
  * this builder's `buildAsync()` path. It installs its Command and Event sides
724
865
  * exactly once.
866
+ *
725
867
  * @returns This builder for further configuration.
726
868
  */
727
869
  addCommandDispatcher(dispatcher) {
@@ -747,6 +889,7 @@ export class BoundedContextBuilder {
747
889
  *
748
890
  * @param dispatcher Raw event dispatcher, or a standalone reactor/subscriber.
749
891
  * Standalone receivers require generated receiver metadata and `buildAsync()`.
892
+ *
750
893
  * @returns This builder for further configuration.
751
894
  */
752
895
  addEventDispatcher(dispatcher) {
@@ -791,6 +934,16 @@ export class BoundedContextBuilder {
791
934
  this.#storageFactory = storageFactory;
792
935
  return this;
793
936
  }
937
+ /**
938
+ * Sets the application AI registry before this Bounded Context is built.
939
+ *
940
+ * @param registry Factory-created deployment registry.
941
+ * @returns This builder.
942
+ */
943
+ withAi(registry) {
944
+ this.#aiRegistry = registry;
945
+ return this;
946
+ }
794
947
  /**
795
948
  * Persists internal system events in the paired System Context storage.
796
949
  *
@@ -846,7 +999,7 @@ export class BoundedContextBuilder {
846
999
  throw new Error("Standalone generated handlers require buildAsync().");
847
1000
  }
848
1001
  ContextParts.rejectSyncEntityAssembly(this.#entityTypes);
849
- return this.#buildWith([...this.#repositories], this.#storageFactory ?? new InMemoryStorageFactory());
1002
+ return this.#buildWith([...this.#repositories], this.#storageFactory ?? new InMemoryStorageFactory(), [], this.#aiRegistry);
850
1003
  }
851
1004
  /**
852
1005
  * Builds a context after loading generated metadata for added entity classes
@@ -861,10 +1014,10 @@ export class BoundedContextBuilder {
861
1014
  async buildAsync() {
862
1015
  return this.#buildAsyncWith();
863
1016
  }
864
- async #buildAsyncWith(defaultStorageFactory) {
1017
+ async #buildAsyncWith(defaultStorageFactory, defaultAi) {
865
1018
  const generated = await this.#loadGeneratedArtifacts([...this.#entityTypes]);
866
1019
  const repositories = [...this.#repositories, ...generated.repositories];
867
- const context = this.#buildWith(repositories, this.#storageFactory ?? defaultStorageFactory ?? new InMemoryStorageFactory(), generated.standalone);
1020
+ const context = this.#buildWith(repositories, this.#storageFactory ?? defaultStorageFactory ?? new InMemoryStorageFactory(), generated.standalone, this.#aiRegistry ?? defaultAi);
868
1021
  try {
869
1022
  await ContextParts.integrationReady(context);
870
1023
  }
@@ -879,19 +1032,22 @@ export class BoundedContextBuilder {
879
1032
  }
880
1033
  return context;
881
1034
  }
882
- #buildWith(repositories, storageFactory, standalone = []) {
1035
+ #buildWith(repositories, storageFactory, standalone = [], ai) {
883
1036
  const resources = this.#subscriptionRegistry === undefined ? {} : { registry: this.#subscriptionRegistry };
884
1037
  this.#subscriptionRegistry = undefined;
885
1038
  const registeredRepositories = [...repositories];
886
1039
  try {
887
- return this.#assembleContext(registeredRepositories, storageFactory, standalone, resources);
1040
+ return this.#assembleContext(registeredRepositories, storageFactory, standalone, resources, ai);
888
1041
  }
889
1042
  catch (error) {
890
1043
  return this.#failBuild(resources, error);
891
1044
  }
892
1045
  }
893
- #assembleContext(repositories, storageFactory, standalone, resources) {
1046
+ #assembleContext(repositories, storageFactory, standalone, resources, ai) {
894
1047
  ContextParts.preflightRepositories(repositories);
1048
+ this.#preflightAgents(repositories, storageFactory, ai);
1049
+ if (ai !== undefined)
1050
+ freezeRegistry(ai);
895
1051
  const dispatchers = this.#buildDispatchers(repositories);
896
1052
  const buses = this.#buildBuses(repositories, storageFactory, dispatchers, resources);
897
1053
  const standaloneEvent = this.#installStandalone(standalone, buses);
@@ -902,13 +1058,56 @@ export class BoundedContextBuilder {
902
1058
  ...ContextParts.standaloneProducedEventSchemas(standalone),
903
1059
  ]);
904
1060
  const running = this.#buildRuntime(storageFactory, buses, resources);
905
- const context = this.#createContext(repositories, storageFactory, buses, running);
1061
+ const context = this.#createContext(repositories, storageFactory, buses, running, ai);
906
1062
  ContextParts.attachIntegration(context, buses.eventBus, buses.systemSpec, ContextParts.externalEventSchemas([
907
1063
  ...dispatchers.domain,
908
1064
  ...(standaloneEvent === undefined ? [] : [standaloneEvent]),
909
1065
  ]));
910
1066
  return context;
911
1067
  }
1068
+ /**
1069
+ * Rejects missing Agent prerequisites before repository intake is installed.
1070
+ *
1071
+ * @param repositories Repositories selected for this Bounded Context.
1072
+ * @param storageFactory Provider supplying mandatory Agent records.
1073
+ * @param ai Effective Bounded Context or server registry.
1074
+ */
1075
+ #preflightAgents(repositories, storageFactory, ai) {
1076
+ const agents = repositories.filter((repository) => repository.entityFamily === "agent");
1077
+ if (agents.length === 0)
1078
+ return;
1079
+ if (ai === undefined)
1080
+ throw new TypeError("Agent registration requires an AI registry.");
1081
+ registryOptions(ai);
1082
+ if (!this.#persistSystemEvents)
1083
+ throw new TypeError("Agent registration requires persisted System Events.");
1084
+ if (!AgentHistoryStorageFactories.supports(storageFactory))
1085
+ throw new TypeError("Agent registration requires indexed history storage.");
1086
+ if (!AgentExecutionStorageFactories.supports(storageFactory))
1087
+ throw new TypeError("Agent registration requires durable execution storage.");
1088
+ this.#checkAgentOutputBindings(agents);
1089
+ for (const repository of agents) {
1090
+ const config = repositoryAccess.agentConfiguration(repository);
1091
+ if (config.codeRevision === undefined || config.codeRevision.trim() === "")
1092
+ throw new TypeError("Agent registration requires a code revision.");
1093
+ if (config.ai === undefined)
1094
+ throw new TypeError("Agent registration requires repository capabilities.");
1095
+ if (config.ai.models.some((model) => !isAiModel(model)))
1096
+ throw new TypeError("Agent repository capabilities must be factory-created models.");
1097
+ }
1098
+ }
1099
+ /**
1100
+ * Rejects Agent outcomes whose raw matching dispatcher cannot bind a saved plan.
1101
+ */
1102
+ #checkAgentOutputBindings(agents) {
1103
+ const rawCommands = new Set([...this.#commandDispatchers].flatMap((dispatcher) => dispatcher.messageSchemas().map((schema) => TypeUrls.derive(schema))));
1104
+ for (const repository of agents) {
1105
+ if (repositoryAccess
1106
+ .producedCommandSchemas(repository)
1107
+ .some((schema) => rawCommands.has(TypeUrls.derive(schema))))
1108
+ throw new TypeError("Agent Command output requires a durable dispatcher binding.");
1109
+ }
1110
+ }
912
1111
  #buildDispatchers(repositories) {
913
1112
  const event = [
914
1113
  ...ContextParts.repositoryEventDispatchers(repositories),
@@ -949,7 +1148,7 @@ export class BoundedContextBuilder {
949
1148
  #buildSystemBuses(storageFactory, dispatchers, resources) {
950
1149
  const systemSpec = ContextParts.createSystemSpec(this.#specSnapshot, this.#persistSystemEvents);
951
1150
  const systemEventStore = systemSpec.storesEvents
952
- ? new EventStore(ContextParts.createStorageMode(systemSpec), storageFactory)
1151
+ ? new EventStore(ContextParts.createStorageMode(systemSpec), storageFactory, systemEventGroup)
953
1152
  : undefined;
954
1153
  if (systemEventStore !== undefined)
955
1154
  resources.systemEventStore = systemEventStore;
@@ -998,8 +1197,8 @@ export class BoundedContextBuilder {
998
1197
  resources.runtime = runtime;
999
1198
  return { stand, registry, runtime };
1000
1199
  }
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);
1200
+ #createContext(repositories, storageFactory, buses, running, ai) {
1201
+ return ContextParts.createBoundedContext(this.#specSnapshot, storageFactory, repositories, this.#deliveryStrategy, buses, running, ai);
1003
1202
  }
1004
1203
  #failBuild(resources, error) {
1005
1204
  const cleanupErrors = [];
@@ -1271,40 +1470,45 @@ const ContextParts = Object.freeze({
1271
1470
  * Creates a bounded context from assembled runtime components.
1272
1471
  *
1273
1472
  * @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
1473
  * @param storageFactory Storage provider.
1283
1474
  * @param repositories Registered repository views.
1284
1475
  * @param deliveryStrategy Delivery sharding strategy.
1476
+ * @param buses Prepared domain and System buses.
1477
+ * @param running Prepared Stand and subscription runtime.
1478
+ * @param ai Effective AI registry when Agent repositories are present.
1285
1479
  * @returns Constructed bounded context.
1286
1480
  */
1287
- createBoundedContext(specSnapshot, commandBus, eventBus, systemEventBus, publisher, stand, systemStand, runtime, systemSpec, storageFactory, repositories, deliveryStrategy) {
1481
+ createBoundedContext(specSnapshot, storageFactory, repositories, deliveryStrategy, buses, running, ai) {
1288
1482
  return constructBoundedContext({
1289
- snapshot: {
1290
- name: specSnapshot.name,
1291
- tenantMode: ContextParts.toTenantMode(specSnapshot.multitenant),
1292
- spec: specSnapshot,
1293
- },
1294
- commandBus,
1295
- eventBus,
1296
- systemEventBus,
1297
- publisher,
1298
- stand,
1299
- systemStand,
1300
- subscriptionRuntime: runtime,
1301
- systemSpec,
1483
+ snapshot: ContextParts.snapshotFor(specSnapshot),
1484
+ commandBus: buses.commandBus,
1485
+ eventBus: buses.eventBus,
1486
+ systemEventBus: buses.systemEventBus,
1487
+ publisher: buses.publisher,
1488
+ stand: running.stand,
1489
+ systemStand: buses.systemStand,
1490
+ subscriptionRuntime: running.runtime,
1491
+ systemSpec: buses.systemSpec,
1302
1492
  storageFactory,
1303
1493
  repositories,
1304
1494
  deliveryStrategy,
1495
+ ...(ai === undefined ? {} : { ai }),
1305
1496
  token: frameworkConstructionToken,
1306
1497
  });
1307
1498
  },
1499
+ /**
1500
+ * Captures public context identity from the build specification.
1501
+ *
1502
+ * @param specSnapshot Prepared context specification.
1503
+ * @returns Immutable context metadata input.
1504
+ */
1505
+ snapshotFor(specSnapshot) {
1506
+ return {
1507
+ name: specSnapshot.name,
1508
+ tenantMode: ContextParts.toTenantMode(specSnapshot.multitenant),
1509
+ spec: specSnapshot,
1510
+ };
1511
+ },
1308
1512
  /**
1309
1513
  * Creates the storage mode represented by a context snapshot.
1310
1514
  *
@@ -2070,7 +2274,7 @@ const ContextParts = Object.freeze({
2070
2274
  */
2071
2275
  isSystemEventDispatcher(dispatcher) {
2072
2276
  const schemas = [...dispatcher.messageSchemas()];
2073
- const systemSchemas = schemas.filter((schema) => schema.typeName.startsWith("spine.system."));
2277
+ const systemSchemas = schemas.filter(isSystemEventSchema);
2074
2278
  if (systemSchemas.length > 0 && systemSchemas.length !== schemas.length) {
2075
2279
  throw new Error("An EventDispatcher cannot mix domain and system event schemas.");
2076
2280
  }
@@ -2272,17 +2476,7 @@ const ContextParts = Object.freeze({
2272
2476
  ContextParts.requireRepositoryInstance(repository, "BoundedContext repository registration");
2273
2477
  const snapshot = ContextParts.repositorySnapshot(repository);
2274
2478
  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
- });
2479
+ ContextParts.bindRepositoryRuntime(repository, registration);
2286
2480
  const entityInboxTarget = repositoryAccess.entityInboxTarget(repository);
2287
2481
  const projectionInboxTarget = repositoryAccess.projectionInboxTarget(repository);
2288
2482
  return {
@@ -2298,6 +2492,32 @@ const ContextParts = Object.freeze({
2298
2492
  },
2299
2493
  };
2300
2494
  },
2495
+ /**
2496
+ * Binds Bounded Context services to a prepared repository before target registration.
2497
+ *
2498
+ * @param repository Prepared repository receiving runtime services.
2499
+ * @param registration Bounded Context services and schemas.
2500
+ */
2501
+ bindRepositoryRuntime(repository, registration) {
2502
+ repositoryAccess.bindRuntime(repository, {
2503
+ context: registration.storageContext,
2504
+ storageFactory: registration.storageFactory,
2505
+ ...(registration.ai === undefined ? {} : { ai: registration.ai }),
2506
+ stand: registration.stand,
2507
+ signalMetadata: new SignalMetadata(),
2508
+ entityInbox: registration.entityInbox,
2509
+ projectionInbox: registration.projectionInbox,
2510
+ publisher: registration.publisher,
2511
+ registerEventSchema: registration.registerEventSchema,
2512
+ registerSystemEventSchema: registration.registerSystemEventSchema,
2513
+ publishAgentEvent: registration.publishAgentEvent,
2514
+ prepareSavedCommand: registration.prepareSavedCommand,
2515
+ publishSavedCommand: registration.publishSavedCommand,
2516
+ publishAgentSystemEvent: registration.publishAgentSystemEvent,
2517
+ recordAcceptedSaved: registration.recordAcceptedSaved,
2518
+ wakeAcceptedAgent: registration.wakeAcceptedAgent,
2519
+ });
2520
+ },
2301
2521
  /**
2302
2522
  * Captures repository metadata needed for context registration.
2303
2523
  *