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

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 (286) hide show
  1. package/README.md +38 -11
  2. package/REFERENCE.md +201 -130
  3. package/dist/agent/agent-acceptance-receipts.d.ts +69 -0
  4. package/dist/agent/agent-acceptance-receipts.d.ts.map +1 -0
  5. package/dist/agent/agent-acceptance-receipts.js +198 -0
  6. package/dist/agent/agent-acceptance-receipts.js.map +1 -0
  7. package/dist/agent/agent-execution-capacity.d.ts +37 -9
  8. package/dist/agent/agent-execution-capacity.d.ts.map +1 -1
  9. package/dist/agent/agent-execution-capacity.js +73 -15
  10. package/dist/agent/agent-execution-capacity.js.map +1 -1
  11. package/dist/agent/agent-history.d.ts +15 -10
  12. package/dist/agent/agent-history.d.ts.map +1 -1
  13. package/dist/agent/agent-history.js +26 -14
  14. package/dist/agent/agent-history.js.map +1 -1
  15. package/dist/agent/agent-live-ai-audit.d.ts +95 -0
  16. package/dist/agent/agent-live-ai-audit.d.ts.map +1 -0
  17. package/dist/agent/agent-live-ai-audit.js +290 -0
  18. package/dist/agent/agent-live-ai-audit.js.map +1 -0
  19. package/dist/agent/agent-live-ai-runtime.d.ts +300 -0
  20. package/dist/agent/agent-live-ai-runtime.d.ts.map +1 -0
  21. package/dist/agent/agent-live-ai-runtime.js +611 -0
  22. package/dist/agent/agent-live-ai-runtime.js.map +1 -0
  23. package/dist/agent/agent-live-audit.d.ts +67 -0
  24. package/dist/agent/agent-live-audit.d.ts.map +1 -0
  25. package/dist/agent/agent-live-audit.js +121 -0
  26. package/dist/agent/agent-live-audit.js.map +1 -0
  27. package/dist/agent/agent-live-budget.d.ts +84 -0
  28. package/dist/agent/agent-live-budget.d.ts.map +1 -0
  29. package/dist/agent/agent-live-budget.js +161 -0
  30. package/dist/agent/agent-live-budget.js.map +1 -0
  31. package/dist/agent/agent-live-fault.d.ts +18 -0
  32. package/dist/agent/agent-live-fault.d.ts.map +1 -0
  33. package/dist/agent/{agent-execution-fault.js → agent-live-fault.js} +7 -7
  34. package/dist/agent/agent-live-fault.js.map +1 -0
  35. package/dist/agent/agent-live-inputs.d.ts +74 -0
  36. package/dist/agent/agent-live-inputs.d.ts.map +1 -0
  37. package/dist/{bus/saved-dispatcher-binding.js → agent/agent-live-inputs.js} +2 -14
  38. package/dist/agent/agent-live-inputs.js.map +1 -0
  39. package/dist/agent/agent-live-interaction-events.d.ts +46 -0
  40. package/dist/agent/agent-live-interaction-events.d.ts.map +1 -0
  41. package/dist/agent/agent-live-interaction-events.js +104 -0
  42. package/dist/agent/agent-live-interaction-events.js.map +1 -0
  43. package/dist/agent/agent-live-mcp-controls.d.ts +129 -0
  44. package/dist/agent/agent-live-mcp-controls.d.ts.map +1 -0
  45. package/dist/agent/agent-live-mcp-controls.js +228 -0
  46. package/dist/agent/agent-live-mcp-controls.js.map +1 -0
  47. package/dist/agent/agent-live-mcp-host.d.ts +88 -0
  48. package/dist/agent/agent-live-mcp-host.d.ts.map +1 -0
  49. package/dist/agent/{agent-mcp-host.js → agent-live-mcp-host.js} +1 -1
  50. package/dist/agent/agent-live-mcp-host.js.map +1 -0
  51. package/dist/agent/{agent-mcp-runtime.d.ts → agent-live-mcp-runtime.d.ts} +6 -6
  52. package/dist/agent/agent-live-mcp-runtime.d.ts.map +1 -0
  53. package/dist/agent/{agent-mcp-runtime.js → agent-live-mcp-runtime.js} +13 -42
  54. package/dist/agent/agent-live-mcp-runtime.js.map +1 -0
  55. package/dist/agent/agent-live-read-runtime.d.ts +61 -0
  56. package/dist/agent/agent-live-read-runtime.d.ts.map +1 -0
  57. package/dist/agent/agent-live-read-runtime.js +121 -0
  58. package/dist/agent/agent-live-read-runtime.js.map +1 -0
  59. package/dist/agent/agent-model-selection.d.ts +21 -34
  60. package/dist/agent/agent-model-selection.d.ts.map +1 -1
  61. package/dist/agent/agent-model-selection.js +61 -78
  62. package/dist/agent/agent-model-selection.js.map +1 -1
  63. package/dist/bus/command-bus.d.ts +14 -18
  64. package/dist/bus/command-bus.d.ts.map +1 -1
  65. package/dist/bus/command-bus.js +31 -48
  66. package/dist/bus/command-bus.js.map +1 -1
  67. package/dist/bus/event-bus.d.ts +17 -4
  68. package/dist/bus/event-bus.d.ts.map +1 -1
  69. package/dist/bus/event-bus.js +42 -12
  70. package/dist/bus/event-bus.js.map +1 -1
  71. package/dist/context/bounded-context.d.ts +140 -87
  72. package/dist/context/bounded-context.d.ts.map +1 -1
  73. package/dist/context/bounded-context.js +702 -329
  74. package/dist/context/bounded-context.js.map +1 -1
  75. package/dist/context/entity-inbox.d.ts +7 -2
  76. package/dist/context/entity-inbox.d.ts.map +1 -1
  77. package/dist/context/entity-inbox.js +22 -13
  78. package/dist/context/entity-inbox.js.map +1 -1
  79. package/dist/context/local-inbox-handoff.d.ts +34 -34
  80. package/dist/context/local-inbox-handoff.d.ts.map +1 -1
  81. package/dist/context/local-inbox-handoff.js +108 -150
  82. package/dist/context/local-inbox-handoff.js.map +1 -1
  83. package/dist/context/projection-handoff.d.ts +2 -2
  84. package/dist/context/projection-handoff.d.ts.map +1 -1
  85. package/dist/context/projection-handoff.js +10 -8
  86. package/dist/context/projection-handoff.js.map +1 -1
  87. package/dist/context/tenant-index.d.ts +36 -31
  88. package/dist/context/tenant-index.d.ts.map +1 -1
  89. package/dist/context/tenant-index.js +123 -125
  90. package/dist/context/tenant-index.js.map +1 -1
  91. package/dist/context/tenant-unknown-error.d.ts +33 -0
  92. package/dist/context/tenant-unknown-error.d.ts.map +1 -0
  93. package/dist/context/tenant-unknown-error.js +75 -0
  94. package/dist/context/tenant-unknown-error.js.map +1 -0
  95. package/dist/delivery/agent-delivery-scope.d.ts +66 -0
  96. package/dist/delivery/agent-delivery-scope.d.ts.map +1 -0
  97. package/dist/delivery/agent-delivery-scope.js +44 -0
  98. package/dist/delivery/agent-delivery-scope.js.map +1 -0
  99. package/dist/delivery/delivery-builder.d.ts +25 -1
  100. package/dist/delivery/delivery-builder.d.ts.map +1 -1
  101. package/dist/delivery/delivery-builder.js +89 -0
  102. package/dist/delivery/delivery-builder.js.map +1 -1
  103. package/dist/delivery/delivery-monitor.d.ts +1 -1
  104. package/dist/delivery/delivery-monitor.js +1 -1
  105. package/dist/delivery/delivery-run-control.d.ts +6 -0
  106. package/dist/delivery/delivery-run-control.d.ts.map +1 -1
  107. package/dist/delivery/delivery-run-control.js +10 -0
  108. package/dist/delivery/delivery-run-control.js.map +1 -1
  109. package/dist/delivery/delivery-supervisor.d.ts.map +1 -1
  110. package/dist/delivery/delivery-supervisor.js +28 -4
  111. package/dist/delivery/delivery-supervisor.js.map +1 -1
  112. package/dist/delivery/delivery-tenant.d.ts +17 -0
  113. package/dist/delivery/delivery-tenant.d.ts.map +1 -0
  114. package/dist/delivery/delivery-tenant.js +50 -0
  115. package/dist/delivery/delivery-tenant.js.map +1 -0
  116. package/dist/delivery/delivery.d.ts +96 -2
  117. package/dist/delivery/delivery.d.ts.map +1 -1
  118. package/dist/delivery/delivery.js +693 -53
  119. package/dist/delivery/delivery.js.map +1 -1
  120. package/dist/delivery/inbox-records.d.ts.map +1 -1
  121. package/dist/delivery/inbox-records.js +9 -16
  122. package/dist/delivery/inbox-records.js.map +1 -1
  123. package/dist/delivery/inbox-storage.d.ts +6 -0
  124. package/dist/delivery/inbox-storage.d.ts.map +1 -1
  125. package/dist/delivery/inbox-storage.js +43 -6
  126. package/dist/delivery/inbox-storage.js.map +1 -1
  127. package/dist/delivery/inbox.d.ts.map +1 -1
  128. package/dist/delivery/inbox.js +3 -2
  129. package/dist/delivery/inbox.js.map +1 -1
  130. package/dist/delivery/reception-observation.d.ts +12 -4
  131. package/dist/delivery/reception-observation.d.ts.map +1 -1
  132. package/dist/delivery/reception-observation.js +14 -3
  133. package/dist/delivery/reception-observation.js.map +1 -1
  134. package/dist/delivery/sharded-work-registry.d.ts +6 -0
  135. package/dist/delivery/sharded-work-registry.d.ts.map +1 -1
  136. package/dist/delivery/sharded-work-registry.js +41 -4
  137. package/dist/delivery/sharded-work-registry.js.map +1 -1
  138. package/dist/entity/entity-storage-descriptor.d.ts.map +1 -1
  139. package/dist/entity/entity-storage-descriptor.js +7 -4
  140. package/dist/entity/entity-storage-descriptor.js.map +1 -1
  141. package/dist/index.d.ts +4 -2
  142. package/dist/index.d.ts.map +1 -1
  143. package/dist/index.js +2 -1
  144. package/dist/index.js.map +1 -1
  145. package/dist/integration/external-messages.d.ts.map +1 -1
  146. package/dist/integration/external-messages.js +9 -15
  147. package/dist/integration/external-messages.js.map +1 -1
  148. package/dist/integration/integration-broker.d.ts.map +1 -1
  149. package/dist/integration/integration-broker.js +112 -6
  150. package/dist/integration/integration-broker.js.map +1 -1
  151. package/dist/integration/third-party-context.d.ts.map +1 -1
  152. package/dist/integration/third-party-context.js +7 -10
  153. package/dist/integration/third-party-context.js.map +1 -1
  154. package/dist/repository/primitive-id.js +26 -11
  155. package/dist/repository/primitive-id.js.map +1 -1
  156. package/dist/repository/repository.d.ts +32 -60
  157. package/dist/repository/repository.d.ts.map +1 -1
  158. package/dist/repository/repository.js +1446 -1787
  159. package/dist/repository/repository.js.map +1 -1
  160. package/dist/runtime/runtime.d.ts +13 -0
  161. package/dist/runtime/runtime.d.ts.map +1 -1
  162. package/dist/runtime/runtime.js +18 -0
  163. package/dist/runtime/runtime.js.map +1 -1
  164. package/dist/runtime/signal-metadata.d.ts.map +1 -1
  165. package/dist/runtime/signal-metadata.js +6 -6
  166. package/dist/runtime/signal-metadata.js.map +1 -1
  167. package/dist/runtime/signal-publisher.d.ts +0 -6
  168. package/dist/runtime/signal-publisher.d.ts.map +1 -1
  169. package/dist/runtime/signal-publisher.js +1 -9
  170. package/dist/runtime/signal-publisher.js.map +1 -1
  171. package/dist/runtime/standalone-handler-runtime.d.ts +0 -7
  172. package/dist/runtime/standalone-handler-runtime.d.ts.map +1 -1
  173. package/dist/runtime/standalone-handler-runtime.js +13 -99
  174. package/dist/runtime/standalone-handler-runtime.js.map +1 -1
  175. package/dist/server/managed-delivery-gate.d.ts +34 -0
  176. package/dist/server/managed-delivery-gate.d.ts.map +1 -0
  177. package/dist/server/managed-delivery-gate.js +70 -0
  178. package/dist/server/managed-delivery-gate.js.map +1 -0
  179. package/dist/server/managed-server-application.d.ts.map +1 -1
  180. package/dist/server/managed-server-application.js +7 -4
  181. package/dist/server/managed-server-application.js.map +1 -1
  182. package/dist/server/server-environment.d.ts +203 -25
  183. package/dist/server/server-environment.d.ts.map +1 -1
  184. package/dist/server/server-environment.js +661 -133
  185. package/dist/server/server-environment.js.map +1 -1
  186. package/dist/server/server-log.d.ts +7 -0
  187. package/dist/server/server-log.d.ts.map +1 -1
  188. package/dist/server/server-log.js +14 -1
  189. package/dist/server/server-log.js.map +1 -1
  190. package/dist/server/server.d.ts +18 -32
  191. package/dist/server/server.d.ts.map +1 -1
  192. package/dist/server/server.js +50 -204
  193. package/dist/server/server.js.map +1 -1
  194. package/dist/services/spine-services.d.ts +1 -1
  195. package/dist/services/spine-services.d.ts.map +1 -1
  196. package/dist/services/spine-services.js +236 -335
  197. package/dist/services/spine-services.js.map +1 -1
  198. package/dist/stand/event-origin-filter.js +3 -3
  199. package/dist/stand/event-origin-filter.js.map +1 -1
  200. package/dist/stand/stand.d.ts +14 -0
  201. package/dist/stand/stand.d.ts.map +1 -1
  202. package/dist/stand/stand.js +53 -9
  203. package/dist/stand/stand.js.map +1 -1
  204. package/dist/stand/subscription-observer.d.ts.map +1 -1
  205. package/dist/stand/subscription-observer.js +54 -40
  206. package/dist/stand/subscription-observer.js.map +1 -1
  207. package/dist/stand/subscription-registry.d.ts +6 -2
  208. package/dist/stand/subscription-registry.d.ts.map +1 -1
  209. package/dist/stand/subscription-registry.js +15 -11
  210. package/dist/stand/subscription-registry.js.map +1 -1
  211. package/dist/stand/subscription-runtime.d.ts +5 -3
  212. package/dist/stand/subscription-runtime.d.ts.map +1 -1
  213. package/dist/stand/subscription-runtime.js +52 -9
  214. package/dist/stand/subscription-runtime.js.map +1 -1
  215. package/dist/testing/index.d.ts.map +1 -1
  216. package/dist/testing/index.js +4 -3
  217. package/dist/testing/index.js.map +1 -1
  218. package/dist/tsconfig.tsbuildinfo +1 -1
  219. package/package.json +14 -14
  220. package/dist/agent/agent-admission.d.ts +0 -80
  221. package/dist/agent/agent-admission.d.ts.map +0 -1
  222. package/dist/agent/agent-admission.js +0 -127
  223. package/dist/agent/agent-admission.js.map +0 -1
  224. package/dist/agent/agent-ai-runtime.d.ts +0 -76
  225. package/dist/agent/agent-ai-runtime.d.ts.map +0 -1
  226. package/dist/agent/agent-ai-runtime.js +0 -1269
  227. package/dist/agent/agent-ai-runtime.js.map +0 -1
  228. package/dist/agent/agent-execution-fault.d.ts +0 -18
  229. package/dist/agent/agent-execution-fault.d.ts.map +0 -1
  230. package/dist/agent/agent-execution-fault.js.map +0 -1
  231. package/dist/agent/agent-execution-session.d.ts +0 -103
  232. package/dist/agent/agent-execution-session.d.ts.map +0 -1
  233. package/dist/agent/agent-execution-session.js +0 -238
  234. package/dist/agent/agent-execution-session.js.map +0 -1
  235. package/dist/agent/agent-interaction-audit.d.ts +0 -58
  236. package/dist/agent/agent-interaction-audit.d.ts.map +0 -1
  237. package/dist/agent/agent-interaction-audit.js +0 -114
  238. package/dist/agent/agent-interaction-audit.js.map +0 -1
  239. package/dist/agent/agent-interaction-events.d.ts +0 -40
  240. package/dist/agent/agent-interaction-events.d.ts.map +0 -1
  241. package/dist/agent/agent-interaction-events.js +0 -129
  242. package/dist/agent/agent-interaction-events.js.map +0 -1
  243. package/dist/agent/agent-mcp-budget.d.ts +0 -125
  244. package/dist/agent/agent-mcp-budget.d.ts.map +0 -1
  245. package/dist/agent/agent-mcp-budget.js +0 -566
  246. package/dist/agent/agent-mcp-budget.js.map +0 -1
  247. package/dist/agent/agent-mcp-host.d.ts +0 -125
  248. package/dist/agent/agent-mcp-host.d.ts.map +0 -1
  249. package/dist/agent/agent-mcp-host.js.map +0 -1
  250. package/dist/agent/agent-mcp-runtime.d.ts.map +0 -1
  251. package/dist/agent/agent-mcp-runtime.js.map +0 -1
  252. package/dist/agent/agent-read-runtime.d.ts +0 -70
  253. package/dist/agent/agent-read-runtime.d.ts.map +0 -1
  254. package/dist/agent/agent-read-runtime.js +0 -320
  255. package/dist/agent/agent-read-runtime.js.map +0 -1
  256. package/dist/agent/agent-revisions.d.ts +0 -35
  257. package/dist/agent/agent-revisions.d.ts.map +0 -1
  258. package/dist/agent/agent-revisions.js +0 -133
  259. package/dist/agent/agent-revisions.js.map +0 -1
  260. package/dist/agent/agent-scheduler.d.ts +0 -99
  261. package/dist/agent/agent-scheduler.d.ts.map +0 -1
  262. package/dist/agent/agent-scheduler.js +0 -398
  263. package/dist/agent/agent-scheduler.js.map +0 -1
  264. package/dist/bus/saved-dispatcher-binding.d.ts +0 -53
  265. package/dist/bus/saved-dispatcher-binding.d.ts.map +0 -1
  266. package/dist/bus/saved-dispatcher-binding.js.map +0 -1
  267. package/dist/delivery/delivery-run-coordinator.d.ts +0 -218
  268. package/dist/delivery/delivery-run-coordinator.d.ts.map +0 -1
  269. package/dist/delivery/delivery-run-coordinator.js +0 -609
  270. package/dist/delivery/delivery-run-coordinator.js.map +0 -1
  271. package/dist/delivery/parked-delivery-obligations.d.ts +0 -182
  272. package/dist/delivery/parked-delivery-obligations.d.ts.map +0 -1
  273. package/dist/delivery/parked-delivery-obligations.js +0 -390
  274. package/dist/delivery/parked-delivery-obligations.js.map +0 -1
  275. package/dist/server/environment-attachment.d.ts +0 -356
  276. package/dist/server/environment-attachment.d.ts.map +0 -1
  277. package/dist/server/environment-attachment.js +0 -2062
  278. package/dist/server/environment-attachment.js.map +0 -1
  279. package/dist/server/environment-delivery-records.d.ts +0 -77
  280. package/dist/server/environment-delivery-records.d.ts.map +0 -1
  281. package/dist/server/environment-delivery-records.js +0 -282
  282. package/dist/server/environment-delivery-records.js.map +0 -1
  283. package/dist/server/environment-delivery-worker.d.ts +0 -161
  284. package/dist/server/environment-delivery-worker.d.ts.map +0 -1
  285. package/dist/server/environment-delivery-worker.js +0 -973
  286. package/dist/server/environment-delivery-worker.js.map +0 -1
@@ -20,36 +20,28 @@ import { freezeRegistry, isAiModel, registryOptions } from "@spine-event-engine/
20
20
  import { TypeUrls } from "@spine-event-engine/core";
21
21
  import { EventSchema, EventContextSchema, BoundedContextNameSchema, TenantIdSchema, } from "@spine-event-engine/proto";
22
22
  import { SPI_type, internal_all, internal_type } from "@spine-event-engine/proto";
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");
23
+ import { ColumnTypes, EventStore, RecordColumn, } from "@spine-event-engine/storage";
24
+ import { AgentHistoryStorageFactories, EventStoreGroups, eventStoreRecordSpec, StoragePreparations, TenantBoundary, } from "@spine-event-engine/storage/provider";
30
25
  import { CommandBus, commandBusAccess } from "../bus/command-bus.js";
31
26
  import { EventBus, eventBusAccess, isSystemEventSchema, } from "../bus/event-bus.js";
32
- import { DeliveryReadiness, } from "./local-inbox-handoff.js";
27
+ import { DeliveryReadiness } from "./local-inbox-handoff.js";
33
28
  import { LocalEntityInbox } from "./entity-inbox.js";
34
29
  import { LocalProjectionInbox } from "./projection-handoff.js";
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
+ import { TenantAdmissions } from "./tenant-unknown-error.js";
31
+ import { AgentExecutionCapacity, } from "../agent/agent-execution-capacity.js";
32
+ import { AgentAcceptanceReceipts, agentAcceptanceReceiptGroup, agentAcceptanceReceiptSpec, } from "../agent/agent-acceptance-receipts.js";
39
33
  import { Repository, repositoryAccess, } from "../repository/repository.js";
40
34
  import { GeneratedRegistryDiscovery } from "../handler/generated-registry-discovery.js";
41
35
  import { HandlerRegistryIngestor, } from "../handler/generated-handler-registry.js";
42
36
  import { AbstractAssignee, AbstractCommander, AbstractEventReactor, AbstractEventSubscriber, } from "../handler/standalone.js";
43
37
  import { HandlerMetadataRegistry, } from "../handler/handler-metadata.js";
44
38
  import { SignalMetadata } from "../runtime/signal-metadata.js";
45
- import { Stand } from "../stand/stand.js";
39
+ import { runtimeAccess } from "../runtime/runtime.js";
40
+ import { Stand, standAccess } from "../stand/stand.js";
46
41
  import { SubscriptionRuntime, subscriptionRuntimeAccess } from "../stand/subscription-runtime.js";
47
- import { StorageSubscriptionRegistry, } from "../stand/subscription-registry.js";
48
- import { UniformAcrossAllShards } from "../delivery/delivery-builder.js";
49
- import { InboxTargets } from "../delivery/inbox.js";
50
- import { ShardIndex } from "../delivery/shard-index.js";
42
+ import { StorageSubscriptionRegistry, subscriptionRecordSpec, } from "../stand/subscription-registry.js";
51
43
  import { IntegrationBroker } from "../integration/integration-broker.js";
52
- import { ServerEnvironment } from "../server/server-environment.js";
44
+ import { ServerEnvironment, serverEnvironmentAccess } from "../server/server-environment.js";
53
45
  import { SignalPublisher } from "../runtime/signal-publisher.js";
54
46
  import { StandaloneHandlerRuntime, } from "../runtime/standalone-handler-runtime.js";
55
47
  /**
@@ -73,6 +65,38 @@ export class BoundedContextNameError extends Error {
73
65
  Object.setPrototypeOf(this, new.target.prototype);
74
66
  }
75
67
  }
68
+ /**
69
+ * Rejects an Entity state type already served by another open domain context.
70
+ */
71
+ export class DuplicateEntityStateException extends Error {
72
+ // prettier-ignore
73
+ /**
74
+ * Wire type URL of the conflicting Entity state.
75
+ */
76
+ stateType;
77
+ /**
78
+ * Name of the context that already serves the state.
79
+ */
80
+ registeredContext;
81
+ /**
82
+ * Name of the context attempting registration.
83
+ */
84
+ conflictingContext;
85
+ /**
86
+ * Creates a duplicate-state registration error.
87
+ * @param stateType Entity state wire type URL.
88
+ * @param registeredContext Existing domain context name.
89
+ * @param conflictingContext New domain context name.
90
+ */
91
+ constructor(stateType, registeredContext, conflictingContext) {
92
+ super(`Entity state "${stateType}" is already registered in "${registeredContext}"; ` +
93
+ `register it in only one open domain Bounded Context (attempted in "${conflictingContext}").`);
94
+ this.name = "DuplicateEntityStateException";
95
+ this.stateType = stateType;
96
+ this.registeredContext = registeredContext;
97
+ this.conflictingContext = conflictingContext;
98
+ }
99
+ }
76
100
  const frameworkConstructionToken = Object.freeze({
77
101
  frameworkConstructionToken: true,
78
102
  });
@@ -87,6 +111,8 @@ const contextTenantIndexes = new WeakMap();
87
111
  const contextStorageFactories = new WeakMap();
88
112
  const contextDeliveryDescriptors = new WeakMap();
89
113
  const contextSubscriptionRuntimes = new WeakMap();
114
+ const contextPreparations = new WeakMap();
115
+ const contextInputPreparations = new WeakMap();
90
116
  const contextLoggers = new WeakMap();
91
117
  const contextSignalPublishers = new WeakMap();
92
118
  const contextEventBuses = new WeakMap();
@@ -97,7 +123,9 @@ const contextClosePhases = new WeakMap();
97
123
  const contextIntegrations = new WeakMap();
98
124
  const systemEventPosters = new WeakMap();
99
125
  const builderBuilds = new WeakMap();
126
+ const explicitPreparations = new WeakMap();
100
127
  let constructBoundedContext;
128
+ let registerBoundedContext;
101
129
  let constructBoundedContextBuilder;
102
130
  let constructContextSpec;
103
131
  /**
@@ -113,22 +141,37 @@ export class BoundedContext {
113
141
  #eventEndpoint;
114
142
  #entityInbox;
115
143
  #projectionInbox;
116
- #deliveryStrategy;
144
+ #deliveryReadiness;
145
+ #stopAgentAvailability;
117
146
  #registeredRepositories = [];
118
147
  #repositoryViews = new Set();
119
148
  #storageFactory;
149
+ #environment;
120
150
  #ai;
121
151
  #stand;
122
152
  #systemStand;
123
153
  #subscriptionRuntime;
124
- #agentScheduler;
125
- #agentErrors = [];
154
+ /**
155
+ * Explicit storage and subscription preparation for this built context.
156
+ */
157
+ runtime;
158
+ #agentReceipts;
159
+ #agentController = new AbortController();
160
+ #agentReservations = new Set();
161
+ #activeReplays = new Set();
162
+ #activePreparations = new Set();
163
+ #tenantPreparations = new Map();
164
+ #preparationController = new AbortController();
165
+ #sharedPreparation;
126
166
  #closed;
127
167
  /**
128
168
  * Registers the framework-only construction hook for this module.
129
169
  */
130
170
  static {
131
171
  constructBoundedContext = (input) => new BoundedContext(input);
172
+ registerBoundedContext = (context, repositories) => {
173
+ context.#registerRepositories(repositories);
174
+ };
132
175
  }
133
176
  /**
134
177
  * Creates a Bounded Context from its prepared framework services.
@@ -145,9 +188,12 @@ export class BoundedContext {
145
188
  this.#stand = input.stand;
146
189
  this.#systemStand = input.systemStand;
147
190
  this.#subscriptionRuntime = input.subscriptionRuntime;
191
+ this.runtime = this.#createRuntime();
148
192
  this.#storageFactory = input.storageFactory;
193
+ this.#agentReceipts = new AgentAcceptanceReceipts(input.storageFactory);
194
+ this.#environment = ServerEnvironment.instance();
149
195
  this.#ai = input.ai;
150
- this.#deliveryStrategy = ContextParts.snapshotDeliveryStrategy(input.deliveryStrategy);
196
+ this.#installTenantAdmission();
151
197
  this.#commandEndpoint = Object.freeze({
152
198
  acceptedCommandTypes: () => this.#commandBus.acceptedCommandTypes(),
153
199
  post: (command) => this.#commandBus.post(command),
@@ -156,79 +202,304 @@ export class BoundedContext {
156
202
  acceptedEventTypes: () => ContextParts.exposedEventTypeUrls(this.#eventBus),
157
203
  post: (event) => ContextParts.postContextEvent(this, event),
158
204
  });
159
- const delivery = this.#createDelivery(input.storageFactory);
205
+ const delivery = this.#createDelivery(input.tenantIndex);
206
+ this.#deliveryReadiness = delivery.deliveryReadiness;
160
207
  this.#entityInbox = delivery.entityInbox;
161
208
  this.#projectionInbox = delivery.projectionInbox;
162
209
  this.#installReferences(input, delivery);
163
- this.#registerAndStart(input.repositories, delivery.tenantIndex);
164
- this.#startAgentScheduler(delivery.tenantIndex);
210
+ serverEnvironmentAccess.addContext(this.#environment, this);
165
211
  Object.freeze(this);
166
212
  }
167
213
  /**
168
- * Starts bounded indexed discovery for registered Agent repositories.
214
+ * Exposes explicit preparation for this built context.
215
+ *
216
+ * @returns Stable explicit preparation methods.
169
217
  */
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);
218
+ #createRuntime() {
219
+ return Object.freeze({
220
+ createStorageStructures: (...ids) => this.#explicitStructures("create", ids),
221
+ checkStructureCompatibility: (...ids) => this.#explicitStructures("check", ids),
222
+ startSubscriptionMonitoring: () => this.#explicitMonitoring(),
192
223
  });
193
- this.#agentScheduler.start();
194
224
  }
195
225
  /**
196
- * Builds one scan scope from an accepted handoff or a catalog sweep.
226
+ * Installs live membership checks on context signal buses.
197
227
  *
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
228
  */
202
- #agentScope(repository, tenantId) {
203
- const effective = EffectiveTenants.current(this.#snapshot.tenantMode === "multitenant", tenantId);
229
+ #installTenantAdmission() {
230
+ explicitPreparations.set(this, (mode, ids) => this.#prepareExplicitLocal(mode, ids));
231
+ commandBusAccess.bindEnvironment(this.#commandBus, this.#environment);
232
+ eventBusAccess.bindEnvironment(this.#eventBus, this.#environment);
233
+ eventBusAccess.bindEnvironment(this.#systemEventBus, this.#environment);
234
+ commandBusAccess.setAdmission(this.#commandBus, (command) => this.#admitTenant(command.context?.actorContext?.tenantId));
235
+ eventBusAccess.setAdmission(this.#eventBus, (event) => this.#admitTenant(ContextParts.readReplayTenant(event)));
236
+ standAccess.setAdmission(this.#stand, (tenantId) => this.#admitTenant(tenantId));
237
+ standAccess.setPublicAdmission(this.#stand, () => {
238
+ if (closingContexts.has(this) && !runtimeAccess.isRunningAdmittedWork(this.#environment))
239
+ throw new Error("Bounded Context is closing.");
240
+ });
241
+ }
242
+ /**
243
+ * Checks this context's live index before repository work.
244
+ *
245
+ * @param tenantId Captured tenant identity, when applicable.
246
+ */
247
+ async #admitTenant(tenantId) {
248
+ await TenantAdmissions.require(this.tenantIndex, this.#snapshot.name.value, this.isMultitenant, tenantId);
249
+ await this.#prepareInput(tenantId);
250
+ }
251
+ /**
252
+ * Initializes only the admitted input's complete context and tenant requirements.
253
+ * @param tenantId Captured input tenant identity, when applicable.
254
+ * @returns Completion after storage and first monitoring pass are ready.
255
+ */
256
+ async #prepareInput(tenantId) {
257
+ await this.#prepareShared("create");
258
+ await this.#prepareTenant(tenantId, "create");
259
+ if (!closingContexts.has(this))
260
+ await this.#subscriptionRuntime.start();
261
+ }
262
+ /**
263
+ * Prepares the fixed shared subscription registry layout once per context.
264
+ * @param mode Required provider preparation mode.
265
+ * @returns Shared preparation completion.
266
+ */
267
+ #prepareShared(mode) {
268
+ if (!(this.#subscriptionRuntime.registry() instanceof StorageSubscriptionRegistry))
269
+ return Promise.resolve();
270
+ if (this.#sharedPreparation !== undefined)
271
+ return this.#sharedPreparation;
272
+ const attempt = this.#trackPreparation(() => StoragePreparations.for(this.#storageFactory).prepareRecord(ContextParts.createSubscriptionStorageContext(this.#snapshot.spec), subscriptionRecordSpec, undefined, mode));
273
+ this.#sharedPreparation = attempt;
274
+ void attempt.catch(() => {
275
+ if (this.#sharedPreparation === attempt)
276
+ this.#sharedPreparation = undefined;
277
+ });
278
+ return attempt;
279
+ }
280
+ /**
281
+ * Shares one pending complete-tenant preparation without caching a stale model flag.
282
+ * @param tenantId Captured tenant identity, when applicable.
283
+ * @param mode Required provider preparation mode.
284
+ * @returns Complete tenant preparation attempt.
285
+ */
286
+ #prepareTenant(tenantId, mode) {
287
+ const context = this.#storageContextFor(tenantId);
288
+ const key = TenantBoundary.of(context).key;
289
+ const pending = this.#tenantPreparations.get(key);
290
+ if (pending !== undefined)
291
+ return pending;
292
+ const attempt = this.#trackPreparation(() => this.#prepareTenantNow(context, mode));
293
+ this.#tenantPreparations.set(key, attempt);
294
+ void attempt.then(() => this.#tenantPreparations.delete(key), () => this.#tenantPreparations.delete(key));
295
+ return attempt;
296
+ }
297
+ /**
298
+ * Prepares exact Event, Entity and Agent families for one captured destination.
299
+ * @param context Captured physical storage destination.
300
+ * @param mode Required provider preparation mode.
301
+ * @returns Completion after all declared families are ready.
302
+ */
303
+ async #prepareTenantNow(context, mode) {
304
+ const preparation = StoragePreparations.for(this.#storageFactory);
305
+ await preparation.prepareRecord(context, eventStoreRecordSpec, EventStoreGroups.forContext(context.name), mode);
306
+ const system = ContextParts.requireSystemPairing(this).system;
307
+ if (system.storesEvents) {
308
+ const systemContext = { ...context, name: system.name.value };
309
+ await preparation.prepareRecord(systemContext, eventStoreRecordSpec, EventStoreGroups.forContext(system.name.value, "system"), mode);
310
+ }
311
+ for (const repository of this.#repositoryViews)
312
+ await repositoryAccess.prepareStorage(repository, context, this.#storageFactory, mode);
313
+ if ([...this.#repositoryViews].some((repository) => repository.entityFamily === "agent"))
314
+ await preparation.prepareRecord(context, agentAcceptanceReceiptSpec, agentAcceptanceReceiptGroup, mode);
315
+ }
316
+ /**
317
+ * Captures the same tenant scope used by repository and Event storage.
318
+ * @param tenantId Complete selected tenant identity, when applicable.
319
+ * @returns A detached provider storage context.
320
+ */
321
+ #storageContextFor(tenantId) {
322
+ if (!this.isMultitenant)
323
+ return { name: this.name.value, multitenant: false };
324
+ if (tenantId === undefined)
325
+ throw new Error("Multitenant preparation requires tenantId.");
204
326
  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),
327
+ name: this.name.value,
328
+ multitenant: true,
329
+ tenantId: TenantBoundary.from(tenantId).tenantId,
212
330
  };
213
331
  }
332
+ /**
333
+ * Retains an admitted preparation until closure can safely release its provider.
334
+ * @param onPrepare Complete provider preparation callback.
335
+ * @returns Tracked preparation completion.
336
+ */
337
+ #trackPreparation(onPrepare) {
338
+ const attempt = Promise.resolve().then(onPrepare);
339
+ this.#activePreparations.add(attempt);
340
+ void attempt.then(() => this.#activePreparations.delete(attempt), () => this.#activePreparations.delete(attempt));
341
+ return attempt;
342
+ }
343
+ /**
344
+ * Completes independently requested eager structure and monitor phases.
345
+ * @param options Independent build-time initialization choices.
346
+ * @returns Completion after requested phases settle.
347
+ */
348
+ async #prepareBuild(options) {
349
+ if (options.createStorageStructures !== false) {
350
+ await this.#requiredPrepare(() => this.#prepareShared("create"));
351
+ await this.#prepareCurrentTenants("create");
352
+ }
353
+ if (options.startSubscriptionMonitoring !== false) {
354
+ await this.#requiredPrepare(() => this.#prepareShared("create"));
355
+ await this.#requiredPrepare(() => this.#subscriptionRuntime.start());
356
+ }
357
+ }
358
+ /**
359
+ * Classifies a requested startup phase without changing shared lazy attempts.
360
+ * @param onPrepare Required provider or initial-monitor phase.
361
+ * @returns Completion of the requested phase.
362
+ */
363
+ async #requiredPrepare(onPrepare) {
364
+ serverEnvironmentAccess.requireAvailable(this.#environment);
365
+ try {
366
+ await onPrepare();
367
+ }
368
+ catch (error) {
369
+ serverEnvironmentAccess.failStartup(this.#environment, error);
370
+ throw error;
371
+ }
372
+ serverEnvironmentAccess.requireAvailable(this.#environment);
373
+ }
374
+ /**
375
+ * Traverses the current live index in finite pages without a tenant cap.
376
+ * @param mode Creation or read-only check.
377
+ * @returns Completion after currently listed tenants are prepared.
378
+ */
379
+ async #prepareCurrentTenants(mode) {
380
+ if (!this.isMultitenant)
381
+ return this.#requiredPrepare(() => this.#prepareTenant(undefined, mode));
382
+ let after;
383
+ do {
384
+ serverEnvironmentAccess.requireAvailable(this.#environment);
385
+ const page = await this.tenantIndex.page({
386
+ count: 64,
387
+ signal: this.#preparationController.signal,
388
+ ...(after === undefined ? {} : { after }),
389
+ });
390
+ serverEnvironmentAccess.requireAvailable(this.#environment);
391
+ for (const id of page.ids) {
392
+ serverEnvironmentAccess.requireAvailable(this.#environment);
393
+ await TenantAdmissions.require(this.tenantIndex, this.name.value, true, id);
394
+ serverEnvironmentAccess.requireAvailable(this.#environment);
395
+ await this.#requiredPrepare(() => this.#prepareTenant(id, mode));
396
+ }
397
+ after = page.hasMore ? page.after : undefined;
398
+ } while (after !== undefined);
399
+ }
400
+ /**
401
+ * Admits explicit structure work and joins fatal cleanup after local settlement.
402
+ * @param mode Creation or read-only compatibility mode.
403
+ * @param tenantIds Explicit complete tenant identities, if supplied.
404
+ * @returns Completion after the selected structures are ready.
405
+ */
406
+ async #explicitStructures(mode, tenantIds) {
407
+ const captured = tenantIds.map((id) => TenantBoundary.from(id).tenantId);
408
+ const admitted = boundedContextAccess.admitServiceRegistryOperation(this, () => this.#prepareExplicitLocal(mode, captured));
409
+ return this.#joinRequiredCleanup(admitted);
410
+ }
411
+ /**
412
+ * Runs explicit preparation while the caller holds this context's admission.
413
+ *
414
+ * @param mode Creation or read-only check.
415
+ * @param ids Captured tenant identities, when supplied.
416
+ * @returns Completion of the local preparation.
417
+ */
418
+ async #prepareExplicitLocal(mode, ids) {
419
+ const selected = await this.#selectedTenants(ids);
420
+ await this.#requiredPrepare(() => this.#prepareShared(mode));
421
+ if (selected === undefined)
422
+ return this.#prepareCurrentTenants(mode);
423
+ for (const id of selected) {
424
+ await TenantAdmissions.require(this.tenantIndex, this.name.value, this.isMultitenant, id);
425
+ await this.#requiredPrepare(() => this.#prepareTenant(id, mode));
426
+ }
427
+ }
428
+ /**
429
+ * Validates all explicit IDs before any provider preparation.
430
+ *
431
+ * @param ids Captured explicit tenant identities.
432
+ * @returns Distinct current IDs or undefined for all current tenants.
433
+ */
434
+ async #selectedTenants(ids) {
435
+ if (ids.length === 0)
436
+ return undefined;
437
+ const selected = new Map();
438
+ for (const id of ids) {
439
+ await TenantAdmissions.require(this.tenantIndex, this.name.value, this.isMultitenant, id);
440
+ const boundary = TenantBoundary.from(id);
441
+ selected.set(boundary.key, boundary.tenantId);
442
+ }
443
+ return [...selected.values()];
444
+ }
445
+ /**
446
+ * Starts the one initial monitor pass as an explicit required phase.
447
+ *
448
+ * @returns Completion after the initial pass.
449
+ */
450
+ #explicitMonitoring() {
451
+ const admitted = boundedContextAccess.admitServiceRegistryOperation(this, async () => {
452
+ await this.#requiredPrepare(() => this.#prepareShared("create"));
453
+ await this.#requiredPrepare(() => this.#subscriptionRuntime.start());
454
+ });
455
+ return this.#joinRequiredCleanup(admitted);
456
+ }
457
+ /**
458
+ * Waits for recorded fatal cleanup only after context admission settles.
459
+ *
460
+ * @param admitted Locally admitted operation.
461
+ * @returns Completion after local work or fatal cleanup.
462
+ */
463
+ async #joinRequiredCleanup(admitted) {
464
+ try {
465
+ await admitted;
466
+ }
467
+ catch (error) {
468
+ if (serverEnvironmentAccess.hasStartupFailure(this.#environment))
469
+ return serverEnvironmentAccess.awaitStartupFailure(this.#environment);
470
+ throw error;
471
+ }
472
+ }
214
473
  /**
215
474
  * Creates tenant-aware Inboxes and their shared readiness signal.
216
475
  *
217
- * @param storageFactory Provider supplying the tenant catalog.
476
+ * @param tenantIndex Effective membership index for this context.
218
477
  * @returns Inboxes, tenant index, and readiness signal.
219
478
  */
220
- #createDelivery(storageFactory) {
221
- const deliveryReadiness = new DeliveryReadiness();
222
- const tenantIndex = TenantIndexes.create({
223
- contextName: this.#snapshot.name.value,
224
- tenantMode: this.#snapshot.tenantMode,
225
- storageFactory,
226
- });
227
- const keepTenant = (tenantId) => tenantIndex.keep(tenantId);
228
- const entityInbox = new LocalEntityInbox(this.#snapshot.name.value, deliveryReadiness, keepTenant, this.#deliveryStrategy);
229
- const projectionInbox = new LocalProjectionInbox(this.#snapshot.name.value, deliveryReadiness, keepTenant);
479
+ #createDelivery(tenantIndex) {
480
+ const deliveryReadiness = this.#createReadiness();
481
+ const onAdmit = (tenantId) => this.#admitTenant(tenantId);
482
+ const entityInbox = new LocalEntityInbox(this.#snapshot.name.value, deliveryReadiness, onAdmit, this.#environment.delivery.strategy);
483
+ const projectionInbox = new LocalProjectionInbox(this.#snapshot.name.value, deliveryReadiness, onAdmit);
484
+ deliveryReadiness.configureDirect((message, tenantId) => message.label === "UPDATE_SUBSCRIBER"
485
+ ? projectionInbox.replay(message, tenantId)
486
+ : entityInbox.replay(message, tenantId));
230
487
  return { deliveryReadiness, tenantIndex, entityInbox, projectionInbox };
231
488
  }
489
+ /**
490
+ * Creates readiness for the shared Delivery and this context's Agent capacity.
491
+ * @returns Shared readiness with the Agent capacity wake installed.
492
+ */
493
+ #createReadiness() {
494
+ const readiness = new DeliveryReadiness(undefined, serverEnvironmentAccess.coreDelivery(this.#environment), (shard) => {
495
+ serverEnvironmentAccess.notifyDelivery(this.#environment, shard);
496
+ }, serverEnvironmentAccess.remoteSupervised(this.#environment));
497
+ if (this.#ai !== undefined)
498
+ this.#stopAgentAvailability = AgentExecutionCapacity.for(this.#ai).onAvailable(() => {
499
+ readiness.retryDeferred();
500
+ });
501
+ return readiness;
502
+ }
232
503
  /**
233
504
  * Installs framework references used after context construction.
234
505
  *
@@ -244,35 +515,17 @@ export class BoundedContext {
244
515
  contextTenantIndexes.set(this, delivery.tenantIndex);
245
516
  contextStorageFactories.set(this, input.storageFactory);
246
517
  contextSubscriptionRuntimes.set(this, this.#subscriptionRuntime);
518
+ contextPreparations.set(this, (options) => this.#prepareBuild(options));
519
+ contextInputPreparations.set(this, (tenantId) => this.#prepareInput(tenantId));
247
520
  contextSignalPublishers.set(this, this.#publisher);
248
521
  contextClosePhases.set(this, {
249
522
  begin: () => {
250
523
  this.#beginClose();
251
524
  },
252
525
  drain: () => this.#drainWork(),
526
+ trackReplay: (onReplay) => this.#trackReplay(onReplay),
253
527
  });
254
- contextDeliveryDescriptors.set(this, ContextParts.createDeliveryDescriptor(this.#snapshot, input.storageFactory, delivery.tenantIndex, this.#entityInbox, this.#projectionInbox, delivery.deliveryReadiness));
255
- }
256
- /**
257
- * Registers repositories and starts subscriptions, closing the tenant index on failure.
258
- *
259
- * @param repositories Prepared repositories.
260
- * @param tenantIndex Index to close if registration fails.
261
- */
262
- #registerAndStart(repositories, tenantIndex) {
263
- try {
264
- this.#registerRepositories(repositories);
265
- this.#subscriptionRuntime.start();
266
- }
267
- catch (error) {
268
- try {
269
- ContextParts.cleanupFailedContext(this, tenantIndex);
270
- }
271
- catch (cleanupError) {
272
- throw new AggregateError([error, cleanupError], "Bounded Context build failed, and tenant index cleanup also failed.");
273
- }
274
- throw error;
275
- }
528
+ contextDeliveryDescriptors.set(this, ContextParts.createDeliveryDescriptor(this.#entityInbox, this.#projectionInbox));
276
529
  }
277
530
  #registerRepositories(repositories) {
278
531
  const preparedRepositories = this.#prepareRepositories(repositories);
@@ -280,6 +533,7 @@ export class BoundedContext {
280
533
  for (const preparedRepository of preparedRepositories) {
281
534
  ContextParts.rejectRegisteredRepository(preparedRepository.repository);
282
535
  }
536
+ this.#reserveStates(preparedRepositories);
283
537
  for (const preparedRepository of preparedRepositories) {
284
538
  this.#stand.register(preparedRepository.snapshot.stateSchema, {
285
539
  columns: ContextParts.repositoryColumns(preparedRepository.snapshot),
@@ -299,6 +553,19 @@ export class BoundedContext {
299
553
  this.#failRegistration(error, preparedRepositories);
300
554
  }
301
555
  }
556
+ /**
557
+ * Reserves Entity state types before registration mutates delivery targets.
558
+ *
559
+ * @param repositories Prepared repositories for this context.
560
+ */
561
+ #reserveStates(repositories) {
562
+ for (const repository of repositories) {
563
+ const stateType = TypeUrls.derive(repository.snapshot.stateSchema);
564
+ const prior = serverEnvironmentAccess.reserveState(this.#environment, this, stateType);
565
+ if (prior !== undefined)
566
+ throw new DuplicateEntityStateException(stateType, prior.name.value, this.name.value);
567
+ }
568
+ }
302
569
  #prepareRepositories(repositories) {
303
570
  const registration = this.#repositoryRegistration();
304
571
  const preparedRepositories = [];
@@ -312,14 +579,28 @@ export class BoundedContext {
312
579
  }
313
580
  return preparedRepositories;
314
581
  }
582
+ /**
583
+ * Reserves shared-registry capacity before Agent receipt admission.
584
+ */
585
+ #reserveAgent(key, signal) {
586
+ const reservation = this.#ai === undefined
587
+ ? undefined
588
+ : AgentExecutionCapacity.for(this.#ai).reserve(key, signal);
589
+ if (reservation !== undefined) {
590
+ this.#agentReservations.add(reservation.completed);
591
+ void reservation.completed.then(() => this.#agentReservations.delete(reservation.completed), () => this.#agentReservations.delete(reservation.completed));
592
+ }
593
+ return reservation;
594
+ }
315
595
  /**
316
596
  * Builds callbacks shared by every repository prepared for this Bounded Context.
317
597
  */
318
598
  #repositoryRegistration() {
319
- const registration = {
599
+ return {
320
600
  name: ContextParts.cloneName(this.#snapshot.name),
321
601
  storageContext: ContextParts.createStorageMode(this.#snapshot.spec),
322
602
  storageFactory: this.#storageFactory,
603
+ onAdmit: (tenantId) => this.#admitTenant(tenantId),
323
604
  ...(this.#ai === undefined ? {} : { ai: this.#ai }),
324
605
  stand: this.#stand,
325
606
  entityInbox: this.#entityInbox,
@@ -331,23 +612,38 @@ export class BoundedContext {
331
612
  registerSystemEventSchema: (schema) => {
332
613
  eventBusAccess.registerSchemas(this.#systemEventBus, [schema]);
333
614
  },
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),
615
+ publishAgentEvent: (event) => this.#publishAgentEvent(event),
339
616
  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));
617
+ agentReceipts: this.#agentReceipts,
618
+ agentSignal: this.#agentController.signal,
619
+ reserveAgent: (key, signal) => this.#reserveAgent(key, signal),
620
+ deferAgent: (message, tenantId) => {
621
+ this.#deferAgent(message, tenantId);
345
622
  },
346
623
  };
347
- return registration;
624
+ }
625
+ /**
626
+ * Publishes an Agent event on its selected ordinary or rejection bus.
627
+ */
628
+ #publishAgentEvent(event) {
629
+ return event.context?.rejection === undefined
630
+ ? this.#publisher.publishEvent(event)
631
+ : this.#publisher.publishRejectionEvent(event);
632
+ }
633
+ /**
634
+ * Records a deferred Agent shard for the next available capacity slot.
635
+ */
636
+ #deferAgent(message, tenantId) {
637
+ this.#deliveryReadiness.defer({
638
+ label: message.label,
639
+ targetTypeUrl: message.inboxId.targetTypeUrl,
640
+ shard: message.shard,
641
+ ...(tenantId === undefined ? {} : { tenantId }),
642
+ });
348
643
  }
349
644
  #failRegistration(error, preparedRepositories) {
350
- const closeErrors = ContextParts.closePreparedRepositories(preparedRepositories);
645
+ const uncommitted = preparedRepositories.filter(({ repository }) => !this.#repositoryViews.has(repository));
646
+ const closeErrors = ContextParts.closePreparedRepositories(uncommitted);
351
647
  if (closeErrors.length > 0) {
352
648
  throw new AggregateError([error, ...closeErrors], "Repository registration failed, and prepared repository storage cleanup also failed.");
353
649
  }
@@ -395,6 +691,14 @@ export class BoundedContext {
395
691
  get isMultitenant() {
396
692
  return this.#snapshot.tenantMode === "multitenant";
397
693
  }
694
+ /**
695
+ * Returns the live index configured for this Bounded Context.
696
+ *
697
+ * @returns The effective configured index.
698
+ */
699
+ get tenantIndex() {
700
+ return ContextParts.requireTenantIndex(this);
701
+ }
398
702
  /**
399
703
  * Returns a copy-safe specification used to build this context.
400
704
  *
@@ -539,7 +843,9 @@ export class BoundedContext {
539
843
  */
540
844
  #beginClose() {
541
845
  closingContexts.add(this);
542
- this.#agentScheduler?.stop();
846
+ this.#preparationController.abort(new Error("Bounded Context closed."));
847
+ serverEnvironmentAccess.stopRecipients(this.#environment, this);
848
+ this.#agentController.abort(new Error("Bounded Context closed."));
543
849
  this.#publisher.beginClose();
544
850
  commandBusAccess.beginClose(this.#commandBus);
545
851
  eventBusAccess.beginClose(this.#eventBus);
@@ -565,22 +871,35 @@ export class BoundedContext {
565
871
  */
566
872
  async #closeOnce() {
567
873
  const errors = [];
568
- await this.#closePart(() => this.#agentScheduler?.close(), errors);
874
+ this.#stopAgentAvailability?.();
875
+ this.#stopAgentAvailability = undefined;
569
876
  await this.#closePart(() => this.#drainWork(), errors);
877
+ await this.#drainAdmittedContextWork(errors);
878
+ await this.#closePart(() => this.#deliveryReadiness.close(), errors);
879
+ await this.#closePart(() => ContextParts.drainContextWork(this.#commandBus, this.#eventBus, this.#systemEventBus, this.#publisher), errors);
880
+ await this.#closePart(() => this.#agentReceipts.close(), errors);
570
881
  await this.#closeBuses(errors);
571
882
  await this.#closePart(() => this.#stand.close(), errors);
572
883
  await this.#closePart(() => this.#systemStand.close(), errors);
573
884
  await this.#closePart(() => this.#subscriptionRuntime.finishClose(), errors);
574
- await this.#closePart(() => {
575
- ContextParts.requireTenantIndex(this).close();
576
- }, errors);
577
885
  await this.#closeRepositories(errors);
886
+ serverEnvironmentAccess.removeContext(this.#environment, this);
578
887
  if (errors.length > 0) {
579
888
  this.#closed = undefined;
580
889
  throw new AggregateError(ContextParts.flattenErrors(errors), "BoundedContext close failed.");
581
890
  }
582
891
  ContextParts.clearContextMetadata(this);
583
892
  }
893
+ /**
894
+ * Settles entered replay, local follow-ups, and reserved Agent turns.
895
+ * @param errors Collects independent close failures.
896
+ */
897
+ async #drainAdmittedContextWork(errors) {
898
+ await this.#closePart(() => Promise.allSettled([...this.#activePreparations]).then(() => undefined), errors);
899
+ await this.#closePart(() => Promise.allSettled([...this.#activeReplays]).then(() => undefined), errors);
900
+ await this.#closePart(() => this.#entityInbox.whenFollowUpsIdle(), errors);
901
+ await this.#closePart(() => Promise.allSettled([...this.#agentReservations]).then(() => undefined), errors);
902
+ }
584
903
  /**
585
904
  * Closes bus intake and subscription drains before the Stands.
586
905
  *
@@ -602,13 +921,27 @@ export class BoundedContext {
602
921
  * @param errors Failures collected during context closure.
603
922
  */
604
923
  async #closeRepositories(errors) {
605
- for (const repository of this.#repositoryViews) {
606
- await this.#closePart(() => {
607
- repositoryAccess.clearRuntime(repository);
924
+ const closing = [...this.#repositoryViews].map((repository) => {
925
+ return repositoryAccess.clearRuntime(repository).finally(() => {
608
926
  registeredRepositories.delete(repository);
609
- }, errors);
927
+ });
928
+ });
929
+ for (const result of await Promise.allSettled(closing)) {
930
+ if (result.status === "rejected")
931
+ errors.push(result.reason);
610
932
  }
611
933
  }
934
+ /**
935
+ * Registers an entered shared replay through its complete follow-up callback.
936
+ * @param onReplay Complete replay callback.
937
+ * @returns Completion of the captured callback.
938
+ */
939
+ #trackReplay(onReplay) {
940
+ const replay = Promise.resolve().then(onReplay);
941
+ this.#activeReplays.add(replay);
942
+ void replay.then(() => this.#activeReplays.delete(replay), () => this.#activeReplays.delete(replay));
943
+ return replay;
944
+ }
612
945
  /**
613
946
  * Calls a close action and collects its failure before later cleanup.
614
947
  * @param work Close action.
@@ -622,6 +955,70 @@ export class BoundedContext {
622
955
  * Exposes framework-only operations for built contexts and their builders.
623
956
  */
624
957
  export const boundedContextAccess = Object.freeze({
958
+ /**
959
+ * Reports whether close has started for the given context.
960
+ *
961
+ * @param context Completed context.
962
+ * @returns Whether close has started.
963
+ */
964
+ isClosing: (context) => closingContexts.has(context),
965
+ /**
966
+ * Prepares an admitted context without joining global fatal cleanup.
967
+ *
968
+ * @param context Admitted context.
969
+ * @param mode Creation or read-only check.
970
+ * @param ids Captured tenant identities.
971
+ * @returns Completion of local preparation.
972
+ */
973
+ prepareExplicit(context, mode, ids) {
974
+ const prepare = explicitPreparations.get(context);
975
+ if (prepare === undefined)
976
+ return Promise.reject(new TypeError("Preparation requires a built context."));
977
+ return prepare(mode, ids);
978
+ },
979
+ /**
980
+ * Admits a service membership and preparation chain before its first await.
981
+ *
982
+ * @param context Selected built context.
983
+ * @param tenantId Captured tenant identity, when applicable.
984
+ * @param onMembership Checks live tenant membership.
985
+ * @param onReady Performs the admitted one-shot registry mutation, if present.
986
+ * @returns Tracked completion of membership and model preparation.
987
+ */
988
+ admitServiceInput(context, tenantId, onMembership, onReady) {
989
+ if (closingContexts.has(context))
990
+ return Promise.reject(new Error("Bounded Context is closing."));
991
+ const prepare = contextInputPreparations.get(context);
992
+ const phase = contextClosePhases.get(context);
993
+ if (prepare === undefined || phase === undefined)
994
+ return Promise.reject(new TypeError("Service input requires a built context."));
995
+ return phase.trackReplay(async () => {
996
+ await onMembership();
997
+ await prepare(tenantId);
998
+ await onReady?.();
999
+ });
1000
+ },
1001
+ /**
1002
+ * Admits one shared subscription-registry operation before asynchronous lookup.
1003
+ *
1004
+ * @param context Selected built context.
1005
+ * @param onOperation Complete registry operation.
1006
+ * @returns Tracked operation completion.
1007
+ */
1008
+ admitServiceRegistryOperation(context, onOperation) {
1009
+ if (closingContexts.has(context))
1010
+ return Promise.reject(new Error("Bounded Context is closing."));
1011
+ const phase = contextClosePhases.get(context);
1012
+ if (phase === undefined)
1013
+ return Promise.reject(new TypeError("Service registry requires a built context."));
1014
+ return phase.trackReplay(onOperation);
1015
+ },
1016
+ trackReplay(context, onReplay) {
1017
+ const phase = contextClosePhases.get(context);
1018
+ if (phase === undefined)
1019
+ return Promise.reject(new TypeError("Replay requires a built context."));
1020
+ return phase.trackReplay(onReplay);
1021
+ },
625
1022
  /**
626
1023
  * Resolves a view only within the Bounded Context that issued it.
627
1024
  *
@@ -669,7 +1066,7 @@ export const boundedContextAccess = Object.freeze({
669
1066
  if (runtime === undefined) {
670
1067
  throw new TypeError("Context logger requires a built BoundedContext instance.");
671
1068
  }
672
- subscriptionRuntimeAccess.installLogger(runtime, logger);
1069
+ subscriptionRuntimeAccess.installLogger(runtime, logger, context.name.value);
673
1070
  },
674
1071
  loggerFor(context) {
675
1072
  const logger = contextLoggers.get(context);
@@ -683,12 +1080,12 @@ export const boundedContextAccess = Object.freeze({
683
1080
  value !== null &&
684
1081
  builderBuilds.has(value));
685
1082
  },
686
- build(builder, defaultStorageFactory, defaultAi) {
1083
+ build(builder) {
687
1084
  const build = builderBuilds.get(builder);
688
1085
  if (build === undefined) {
689
1086
  throw new TypeError("Builder access requires a BoundedContextBuilder instance.");
690
1087
  }
691
- return build(defaultStorageFactory, defaultAi);
1088
+ return build();
692
1089
  },
693
1090
  subscribeToEvent(context, typeUrl, subscriber) {
694
1091
  const subscribe = eventSubscribers.get(context);
@@ -765,9 +1162,8 @@ export class BoundedContextBuilder {
765
1162
  #repositories = new Set();
766
1163
  #entityTypes = new Set();
767
1164
  #generatedRepositoryOptions = new Map();
768
- #deliveryStrategy = UniformAcrossAllShards.singleShard();
769
- #storageFactory;
770
1165
  #aiRegistry;
1166
+ #tenantIndex;
771
1167
  #subscriptionRegistry;
772
1168
  #persistSystemEvents = false;
773
1169
  #generatedRegistryRoot;
@@ -786,7 +1182,7 @@ export class BoundedContextBuilder {
786
1182
  constructor(specSnapshot, token) {
787
1183
  ContextParts.requireFrameworkConstructionToken(token, "BoundedContextBuilder instances are framework-owned.");
788
1184
  this.#specSnapshot = ContextParts.cloneSpecSnapshot(specSnapshot);
789
- builderBuilds.set(this, (defaultStorageFactory, defaultAi) => this.#buildAsyncWith(defaultStorageFactory, defaultAi));
1185
+ builderBuilds.set(this, () => this.#buildAsyncWith());
790
1186
  Object.freeze(this);
791
1187
  }
792
1188
  /**
@@ -861,7 +1257,7 @@ export class BoundedContextBuilder {
861
1257
  *
862
1258
  * @param dispatcher Raw command dispatcher, or an `AbstractCommander` instance.
863
1259
  * A standalone commander requires generated receiver metadata and therefore
864
- * this builder's `buildAsync()` path. It installs its Command and Event sides
1260
+ * this builder's `build()` path. It installs its Command and Event sides
865
1261
  * exactly once.
866
1262
  *
867
1263
  * @returns This builder for further configuration.
@@ -888,7 +1284,7 @@ export class BoundedContextBuilder {
888
1284
  * Adds an event dispatcher to the context being built.
889
1285
  *
890
1286
  * @param dispatcher Raw event dispatcher, or a standalone reactor/subscriber.
891
- * Standalone receivers require generated receiver metadata and `buildAsync()`.
1287
+ * Standalone receivers require generated receiver metadata and `build()`.
892
1288
  *
893
1289
  * @returns This builder for further configuration.
894
1290
  */
@@ -905,7 +1301,7 @@ export class BoundedContextBuilder {
905
1301
  * Adds a generated standalone command assignee.
906
1302
  *
907
1303
  * The registered instance is matched by exact constructor to generated
908
- * receiver metadata when `buildAsync()` assembles the context.
1304
+ * receiver metadata when `build()` assembles the context.
909
1305
  *
910
1306
  * @param assignee Generated standalone assignee instance.
911
1307
  * @returns This builder for further configuration.
@@ -925,13 +1321,13 @@ export class BoundedContextBuilder {
925
1321
  return this;
926
1322
  }
927
1323
  /**
928
- * Sets a storage factory for event, repository-state, and Stand storage.
1324
+ * Sets a live tenant index for this Bounded Context.
929
1325
  *
930
- * @param storageFactory Creates the context's persistent storage.
931
- * @returns Returns this builder for further configuration.
1326
+ * @param tenantIndex Application membership index.
1327
+ * @returns This builder.
932
1328
  */
933
- withStorageFactory(storageFactory) {
934
- this.#storageFactory = storageFactory;
1329
+ withTenantIndex(tenantIndex) {
1330
+ this.#tenantIndex = tenantIndex;
935
1331
  return this;
936
1332
  }
937
1333
  /**
@@ -969,16 +1365,6 @@ export class BoundedContextBuilder {
969
1365
  this.#subscriptionRegistry = registry;
970
1366
  return this;
971
1367
  }
972
- /**
973
- * Sets how Entity Inbox targets are assigned to shards.
974
- *
975
- * @param strategy Selects the durable shard for Aggregate and Process Manager targets.
976
- * @returns Returns this builder for further configuration.
977
- */
978
- withDeliveryStrategy(strategy) {
979
- this.#deliveryStrategy = ContextParts.snapshotDeliveryStrategy(strategy);
980
- return this;
981
- }
982
1368
  /**
983
1369
  * Sets a trusted compiled application root for generated handler metadata.
984
1370
  *
@@ -990,49 +1376,108 @@ export class BoundedContextBuilder {
990
1376
  return this;
991
1377
  }
992
1378
  /**
993
- * Builds a context from explicitly added repositories and dispatchers.
1379
+ * Builds a context after loading generated handlers and preparing runtime resources.
994
1380
  *
995
- * @returns Returns the built context.
1381
+ * @param options Independent eager structure and subscription timing choices.
1382
+ * @returns The built context after requested initialization and integration are ready.
996
1383
  */
997
- build() {
998
- if (this.#standaloneInstances().length > 0) {
999
- throw new Error("Standalone generated handlers require buildAsync().");
1384
+ build(options = {}) {
1385
+ return this.#buildAsyncWith(options);
1386
+ }
1387
+ async #buildAsyncWith(options = {}) {
1388
+ const environment = ServerEnvironment.instance();
1389
+ const finishBuild = serverEnvironmentAccess.beginContextBuild(environment);
1390
+ let built;
1391
+ let failed = false;
1392
+ let failure;
1393
+ try {
1394
+ await serverEnvironmentAccess.awaitSetup(environment);
1395
+ built = await this.#buildAsyncReady(options, environment);
1396
+ }
1397
+ catch (error) {
1398
+ failed = true;
1399
+ failure = error;
1400
+ }
1401
+ finally {
1402
+ finishBuild();
1000
1403
  }
1001
- ContextParts.rejectSyncEntityAssembly(this.#entityTypes);
1002
- return this.#buildWith([...this.#repositories], this.#storageFactory ?? new InMemoryStorageFactory(), [], this.#aiRegistry);
1404
+ if (serverEnvironmentAccess.hasStartupFailure(environment))
1405
+ return serverEnvironmentAccess.awaitStartupFailure(environment);
1406
+ if (failed)
1407
+ throw failure;
1408
+ if (built === undefined)
1409
+ throw new Error("Bounded Context build did not complete.");
1410
+ return built;
1003
1411
  }
1004
1412
  /**
1005
- * Builds a context after loading generated metadata for added entity classes
1006
- * and registered standalone handlers.
1413
+ * Loads generated metadata and finishes one asynchronous context assembly.
1007
1414
  *
1008
- * Standalone assignees, commanders, reactors, and subscribers require this
1009
- * asynchronous path so their exact constructors can be matched to generated
1010
- * receiver metadata.
1011
- *
1012
- * @returns Resolves to the built context.
1415
+ * @returns The integrated context.
1013
1416
  */
1014
- async buildAsync() {
1015
- return this.#buildAsyncWith();
1016
- }
1017
- async #buildAsyncWith(defaultStorageFactory, defaultAi) {
1417
+ async #buildAsyncReady(options, environment) {
1018
1418
  const generated = await this.#loadGeneratedArtifacts([...this.#entityTypes]);
1419
+ serverEnvironmentAccess.requireAvailable(environment);
1019
1420
  const repositories = [...this.#repositories, ...generated.repositories];
1020
- const context = this.#buildWith(repositories, this.#storageFactory ?? defaultStorageFactory ?? new InMemoryStorageFactory(), generated.standalone, this.#aiRegistry ?? defaultAi);
1421
+ const context = await this.#buildWithResources(repositories, environment.storageFactory, generated.standalone, this.#aiRegistry ?? environment.ai);
1021
1422
  try {
1022
- await ContextParts.integrationReady(context);
1423
+ await this.#integrateBuiltContext(context, options, environment);
1023
1424
  }
1024
1425
  catch (error) {
1025
- try {
1026
- await context.close();
1027
- }
1028
- catch (closeError) {
1029
- throw new AggregateError([error, closeError], "BoundedContext broker open failed during cleanup.");
1030
- }
1031
- throw error;
1426
+ return this.#closeFailedBuild(context, environment, error);
1032
1427
  }
1033
1428
  return context;
1034
1429
  }
1035
- #buildWith(repositories, storageFactory, standalone = [], ai) {
1430
+ /**
1431
+ * Completes required integration and requested initialization before publication.
1432
+ * @param context Constructed context.
1433
+ * @param options Requested eager initialization phases.
1434
+ * @param environment Captured environment lifetime.
1435
+ */
1436
+ async #integrateBuiltContext(context, options, environment) {
1437
+ serverEnvironmentAccess.requireAvailable(environment);
1438
+ boundedContextAccess.installLogger(context, serverEnvironmentAccess.loggerFor(environment));
1439
+ await ContextParts.integrationReady(context).catch((error) => {
1440
+ serverEnvironmentAccess.failStartup(environment, error);
1441
+ throw error;
1442
+ });
1443
+ serverEnvironmentAccess.requireAvailable(environment);
1444
+ const prepare = contextPreparations.get(context);
1445
+ if (prepare === undefined)
1446
+ throw new Error("Context preparation is unavailable.");
1447
+ await prepare(options);
1448
+ serverEnvironmentAccess.requireAvailable(environment);
1449
+ await serverEnvironmentAccess.completeContext(environment, context);
1450
+ }
1451
+ /**
1452
+ * Releases a failed partial context before its build ticket settles.
1453
+ * @param context Context assembled before the failure.
1454
+ * @param environment Captured environment lifetime.
1455
+ * @param error Original build failure.
1456
+ * @returns Rejection retaining the original and any local cleanup failure.
1457
+ */
1458
+ async #closeFailedBuild(context, environment, error) {
1459
+ try {
1460
+ await context.close();
1461
+ }
1462
+ catch (closeError) {
1463
+ if (serverEnvironmentAccess.hasStartupFailure(environment)) {
1464
+ serverEnvironmentAccess.recordStartupCleanup(environment, closeError);
1465
+ throw error;
1466
+ }
1467
+ throw new AggregateError([error, closeError], "BoundedContext open cleanup failed.");
1468
+ }
1469
+ throw error;
1470
+ }
1471
+ /**
1472
+ * Assembles a context and cleans partial resources on failure.
1473
+ *
1474
+ * @param repositories Repositories selected for the context.
1475
+ * @param storageFactory Storage factory for the context.
1476
+ * @param standalone Generated standalone handler groups.
1477
+ * @param ai Optional AI registry.
1478
+ * @returns The assembled context.
1479
+ */
1480
+ async #buildWithResources(repositories, storageFactory, standalone, ai) {
1036
1481
  const resources = this.#subscriptionRegistry === undefined ? {} : { registry: this.#subscriptionRegistry };
1037
1482
  this.#subscriptionRegistry = undefined;
1038
1483
  const registeredRepositories = [...repositories];
@@ -1040,7 +1485,7 @@ export class BoundedContextBuilder {
1040
1485
  return this.#assembleContext(registeredRepositories, storageFactory, standalone, resources, ai);
1041
1486
  }
1042
1487
  catch (error) {
1043
- return this.#failBuild(resources, error);
1488
+ return await this.#failBuild(resources, error);
1044
1489
  }
1045
1490
  }
1046
1491
  #assembleContext(repositories, storageFactory, standalone, resources, ai) {
@@ -1059,6 +1504,10 @@ export class BoundedContextBuilder {
1059
1504
  ]);
1060
1505
  const running = this.#buildRuntime(storageFactory, buses, resources);
1061
1506
  const context = this.#createContext(repositories, storageFactory, buses, running, ai);
1507
+ resources.context = context;
1508
+ if (registerBoundedContext === undefined)
1509
+ throw new Error("Context registration is unavailable.");
1510
+ registerBoundedContext(context, repositories);
1062
1511
  ContextParts.attachIntegration(context, buses.eventBus, buses.systemSpec, ContextParts.externalEventSchemas([
1063
1512
  ...dispatchers.domain,
1064
1513
  ...(standaloneEvent === undefined ? [] : [standaloneEvent]),
@@ -1083,31 +1532,14 @@ export class BoundedContextBuilder {
1083
1532
  throw new TypeError("Agent registration requires persisted System Events.");
1084
1533
  if (!AgentHistoryStorageFactories.supports(storageFactory))
1085
1534
  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
1535
  for (const repository of agents) {
1090
1536
  const config = repositoryAccess.agentConfiguration(repository);
1091
- if (config.codeRevision === undefined || config.codeRevision.trim() === "")
1092
- throw new TypeError("Agent registration requires a code revision.");
1093
1537
  if (config.ai === undefined)
1094
1538
  throw new TypeError("Agent registration requires repository capabilities.");
1095
1539
  if (config.ai.models.some((model) => !isAiModel(model)))
1096
1540
  throw new TypeError("Agent repository capabilities must be factory-created models.");
1097
1541
  }
1098
1542
  }
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
- }
1111
1543
  #buildDispatchers(repositories) {
1112
1544
  const event = [
1113
1545
  ...ContextParts.repositoryEventDispatchers(repositories),
@@ -1148,7 +1580,7 @@ export class BoundedContextBuilder {
1148
1580
  #buildSystemBuses(storageFactory, dispatchers, resources) {
1149
1581
  const systemSpec = ContextParts.createSystemSpec(this.#specSnapshot, this.#persistSystemEvents);
1150
1582
  const systemEventStore = systemSpec.storesEvents
1151
- ? new EventStore(ContextParts.createStorageMode(systemSpec), storageFactory, systemEventGroup)
1583
+ ? new EventStore(ContextParts.createStorageMode(systemSpec), storageFactory, EventStoreGroups.forContext(systemSpec.name.value, "system"))
1152
1584
  : undefined;
1153
1585
  if (systemEventStore !== undefined)
1154
1586
  resources.systemEventStore = systemEventStore;
@@ -1185,10 +1617,7 @@ export class BoundedContextBuilder {
1185
1617
  return event;
1186
1618
  }
1187
1619
  #buildRuntime(storageFactory, buses, resources) {
1188
- const stand = new Stand({
1189
- context: ContextParts.createStorageMode(this.#specSnapshot),
1190
- storageFactory,
1191
- });
1620
+ const stand = this.#buildStand(storageFactory);
1192
1621
  resources.stand = stand;
1193
1622
  const registry = resources.registry ??
1194
1623
  new StorageSubscriptionRegistry(ContextParts.createSubscriptionStorageContext(this.#specSnapshot), storageFactory);
@@ -1197,19 +1626,50 @@ export class BoundedContextBuilder {
1197
1626
  resources.runtime = runtime;
1198
1627
  return { stand, registry, runtime };
1199
1628
  }
1629
+ /**
1630
+ * Builds the context Stand with a captured live membership directory.
1631
+ *
1632
+ * @param storageFactory Provider used for Entity records.
1633
+ * @returns Direct read-side Stand.
1634
+ */
1635
+ #buildStand(storageFactory) {
1636
+ const tenantIndex = this.#tenantIndex ?? ServerEnvironment.instance().tenantIndex;
1637
+ const stand = new Stand({
1638
+ context: ContextParts.createStorageMode(this.#specSnapshot),
1639
+ storageFactory,
1640
+ });
1641
+ standAccess.setAdmission(stand, (tenantId) => TenantAdmissions.require(tenantIndex, this.#specSnapshot.name.value, this.#specSnapshot.multitenant, tenantId));
1642
+ return stand;
1643
+ }
1200
1644
  #createContext(repositories, storageFactory, buses, running, ai) {
1201
- return ContextParts.createBoundedContext(this.#specSnapshot, storageFactory, repositories, this.#deliveryStrategy, buses, running, ai);
1645
+ return ContextParts.createBoundedContext(this.#specSnapshot, storageFactory, repositories, ServerEnvironment.instance().delivery.strategy, buses, running, ai, this.#tenantIndex ?? ServerEnvironment.instance().tenantIndex);
1202
1646
  }
1203
- #failBuild(resources, error) {
1647
+ async #failBuild(resources, error) {
1204
1648
  const cleanupErrors = [];
1205
- ContextParts.attemptCleanup(() => resources.runtime?.abortClose(), cleanupErrors);
1206
- ContextParts.attemptCleanup(() => resources.publisher?.abortAssembly(), cleanupErrors);
1207
- if (resources.runtime === undefined) {
1208
- ContextParts.attemptCleanup(() => void resources.registry?.close().catch(() => undefined), cleanupErrors);
1209
- }
1210
- ContextParts.attemptCleanup(() => void resources.stand?.close().catch(() => undefined), cleanupErrors);
1211
- ContextParts.attemptCleanup(() => void resources.systemStand?.close().catch(() => undefined), cleanupErrors);
1212
- ContextParts.cleanupBuildBuses(resources, cleanupErrors);
1649
+ if (resources.context !== undefined) {
1650
+ try {
1651
+ await resources.context.close();
1652
+ }
1653
+ catch (closeError) {
1654
+ cleanupErrors.push(closeError);
1655
+ }
1656
+ }
1657
+ else {
1658
+ ContextParts.attemptCleanup(() => resources.publisher?.abortAssembly(), cleanupErrors);
1659
+ const registry = resources.runtime ?? resources.registry;
1660
+ const closing = [registry, resources.stand, resources.systemStand];
1661
+ for (const resource of closing) {
1662
+ if (resource === undefined)
1663
+ continue;
1664
+ try {
1665
+ await resource.close();
1666
+ }
1667
+ catch (closeError) {
1668
+ cleanupErrors.push(closeError);
1669
+ }
1670
+ }
1671
+ await ContextParts.cleanupBuildBuses(resources, cleanupErrors);
1672
+ }
1213
1673
  if (cleanupErrors.length > 0) {
1214
1674
  throw new AggregateError([error, ...cleanupErrors], "Bounded Context build failed during cleanup.");
1215
1675
  }
@@ -1409,24 +1869,29 @@ const ContextParts = Object.freeze({
1409
1869
  *
1410
1870
  * @param resources Partially created resources.
1411
1871
  * @param errors Collection receiving failures.
1412
- */
1413
- cleanupBuildBuses(resources, errors) {
1414
- ContextParts.attemptCleanup(() => {
1415
- if (resources.commandBus !== undefined)
1416
- commandBusAccess.abortClose(resources.commandBus);
1417
- }, errors);
1418
- ContextParts.attemptCleanup(() => {
1419
- if (resources.systemEventBus !== undefined)
1420
- eventBusAccess.abortClose(resources.systemEventBus);
1421
- else
1422
- resources.systemEventStore?.close();
1423
- }, errors);
1424
- ContextParts.attemptCleanup(() => {
1425
- if (resources.eventBus !== undefined)
1426
- eventBusAccess.abortClose(resources.eventBus);
1427
- else
1428
- resources.eventStore?.close();
1429
- }, errors);
1872
+ * @returns Completion after all created buses and stores close.
1873
+ */
1874
+ async cleanupBuildBuses(resources, errors) {
1875
+ const closing = [];
1876
+ const { commandBus, systemEventBus, systemEventStore, eventBus, eventStore } = resources;
1877
+ if (commandBus !== undefined)
1878
+ closing.push(() => commandBus.close());
1879
+ if (systemEventBus !== undefined)
1880
+ closing.push(() => systemEventBus.close());
1881
+ else if (systemEventStore !== undefined)
1882
+ closing.push(() => systemEventStore.close());
1883
+ if (eventBus !== undefined)
1884
+ closing.push(() => eventBus.close());
1885
+ else if (eventStore !== undefined)
1886
+ closing.push(() => eventStore.close());
1887
+ for (const close of closing) {
1888
+ try {
1889
+ await close();
1890
+ }
1891
+ catch (error) {
1892
+ errors.push(error);
1893
+ }
1894
+ }
1430
1895
  },
1431
1896
  /**
1432
1897
  * Returns constituent failures from nested aggregate errors.
@@ -1476,9 +1941,10 @@ const ContextParts = Object.freeze({
1476
1941
  * @param buses Prepared domain and System buses.
1477
1942
  * @param running Prepared Stand and subscription runtime.
1478
1943
  * @param ai Effective AI registry when Agent repositories are present.
1944
+ * @param tenantIndex Effective live membership index.
1479
1945
  * @returns Constructed bounded context.
1480
1946
  */
1481
- createBoundedContext(specSnapshot, storageFactory, repositories, deliveryStrategy, buses, running, ai) {
1947
+ createBoundedContext(specSnapshot, storageFactory, repositories, deliveryStrategy, buses, running, ai, tenantIndex) {
1482
1948
  return constructBoundedContext({
1483
1949
  snapshot: ContextParts.snapshotFor(specSnapshot),
1484
1950
  commandBus: buses.commandBus,
@@ -1490,6 +1956,7 @@ const ContextParts = Object.freeze({
1490
1956
  subscriptionRuntime: running.runtime,
1491
1957
  systemSpec: buses.systemSpec,
1492
1958
  storageFactory,
1959
+ tenantIndex: tenantIndex ?? ServerEnvironment.instance().tenantIndex,
1493
1960
  repositories,
1494
1961
  deliveryStrategy,
1495
1962
  ...(ai === undefined ? {} : { ai }),
@@ -1533,35 +2000,6 @@ const ContextParts = Object.freeze({
1533
2000
  multitenant: false,
1534
2001
  });
1535
2002
  },
1536
- /**
1537
- * Captures the shard count and validates later shard results.
1538
- *
1539
- * @param strategy Configured delivery strategy.
1540
- * @returns Immutable delivery strategy snapshot.
1541
- */
1542
- snapshotDeliveryStrategy(strategy) {
1543
- if (!Number.isSafeInteger(strategy.shardCount) || strategy.shardCount <= 0) {
1544
- throw new Error("Delivery strategy shard count must be a positive safe integer.");
1545
- }
1546
- const shardCount = strategy.shardCount;
1547
- return Object.freeze({
1548
- shardCount,
1549
- /**
1550
- * Returns a target shard after verifying the captured shard total.
1551
- *
1552
- * @param targetId Inbox target identifier.
1553
- * @param targetType Inbox target type.
1554
- * @returns Validated shard index.
1555
- */
1556
- shardFor(targetId, targetType) {
1557
- const shard = strategy.shardFor(InboxTargets.clone(targetId), targetType);
1558
- if (shard.ofTotal !== shardCount) {
1559
- throw new Error("Delivery strategy shard total must equal its resolved shard count.");
1560
- }
1561
- return new ShardIndex(shard.index, shard.ofTotal);
1562
- },
1563
- });
1564
- },
1565
2003
  /**
1566
2004
  * Creates the storage context for read-side catch-up.
1567
2005
  *
@@ -1740,8 +2178,7 @@ const ContextParts = Object.freeze({
1740
2178
  },
1741
2179
  });
1742
2180
  const ready = broker.open();
1743
- // Synchronous build returns before readiness; retain the failure for the next
1744
- // observable operation without letting Node report an unhandled rejection.
2181
+ // The builder awaits readiness; retain a rejection handler until it observes the result.
1745
2182
  void ready.catch(() => undefined);
1746
2183
  contextIntegrations.set(context, { broker, ready });
1747
2184
  },
@@ -1878,21 +2315,9 @@ const ContextParts = Object.freeze({
1878
2315
  */
1879
2316
  requireEntityClass(entityType, operation) {
1880
2317
  if (typeof entityType !== "function") {
1881
- throw new TypeError(`${operation} requires a Repository instance. Use an entity class only with buildAsync().`);
2318
+ throw new TypeError(`${operation} requires a Repository instance. Use an entity class only with build().`);
1882
2319
  }
1883
2320
  },
1884
- /**
1885
- * Rejects synchronous assembly when classes need generated metadata.
1886
- *
1887
- * @param entityTypes Entity classes to assemble.
1888
- */
1889
- rejectSyncEntityAssembly(entityTypes) {
1890
- if (entityTypes.size === 0) {
1891
- return;
1892
- }
1893
- throw new Error("BoundedContextBuilder.build() cannot assemble entity classes from generated metadata. " +
1894
- "Use buildAsync().");
1895
- },
1896
2321
  /**
1897
2322
  * Validates that a root exists when generated metadata must be loaded.
1898
2323
  *
@@ -1903,7 +2328,7 @@ const ContextParts = Object.freeze({
1903
2328
  if (root !== undefined) {
1904
2329
  return root;
1905
2330
  }
1906
- throw new Error("BoundedContextBuilder.buildAsync() requires withGeneratedRegistryRoot(root) " +
2331
+ throw new Error("BoundedContextBuilder.build() requires withGeneratedRegistryRoot(root) " +
1907
2332
  "when assembling entity classes from generated metadata.");
1908
2333
  },
1909
2334
  /**
@@ -2295,16 +2720,6 @@ const ContextParts = Object.freeze({
2295
2720
  }
2296
2721
  return Object.freeze([...schemas.values()]);
2297
2722
  },
2298
- /**
2299
- * Clears metadata for a failed context build and closes its tenant index.
2300
- *
2301
- * @param context Context whose build failed.
2302
- * @param tenantIndex Tenant index to close.
2303
- */
2304
- cleanupFailedContext(context, tenantIndex) {
2305
- ContextParts.clearContextMetadata(context);
2306
- tenantIndex.close();
2307
- },
2308
2723
  /**
2309
2724
  * Removes runtime metadata associated with a context.
2310
2725
  *
@@ -2327,68 +2742,25 @@ const ContextParts = Object.freeze({
2327
2742
  contextStorageFactories.delete(context);
2328
2743
  contextDeliveryDescriptors.delete(context);
2329
2744
  contextSubscriptionRuntimes.delete(context);
2745
+ contextInputPreparations.delete(context);
2746
+ contextPreparations.delete(context);
2330
2747
  contextIntegrations.delete(context);
2331
2748
  eventSubscribers.delete(context);
2332
2749
  systemEventPosters.delete(context);
2333
2750
  },
2334
2751
  /**
2335
- * Creates delivery operations from the context inboxes and readiness state.
2752
+ * Creates delivery operations from the context inboxes.
2336
2753
  *
2337
- * @param context Built context snapshot.
2338
- * @param storageFactory Context storage provider.
2339
- * @param tenantIndex Tenant index for startup scopes.
2340
2754
  * @param entityInbox Registered Entity inbox.
2341
2755
  * @param projections Projection inbox.
2342
- * @param readiness Delivery readiness coordinator.
2343
2756
  * @returns Delivery descriptor.
2344
2757
  */
2345
- createDeliveryDescriptor(context, storageFactory, tenantIndex, entityInbox, projections, readiness) {
2758
+ createDeliveryDescriptor(entityInbox, projections) {
2346
2759
  return Object.freeze({
2347
- storageFactory,
2348
- startupScopes: () => ContextParts.deliveryStartupScopes(tenantIndex),
2349
- storageContext: (scope) => ContextParts.deliveryStorageContext(context, scope),
2350
2760
  endpoints: () => Object.freeze([...entityInbox.endpoints(), ...projections.endpoints()]),
2351
2761
  replay: (message, tenantId) => message.label === "UPDATE_SUBSCRIBER"
2352
2762
  ? projections.replay(message, tenantId)
2353
2763
  : entityInbox.replay(message, tenantId),
2354
- onReady: (onReady) => readiness.onReady(onReady),
2355
- transition: (scopes, onReady, options) => readiness.transition(scopes, onReady, options),
2356
- });
2357
- },
2358
- /**
2359
- * Lists delivery startup scopes, one per tenant in multitenant contexts.
2360
- *
2361
- * @param tenantIndex Tenant index supplying configured scopes.
2362
- * @returns Frozen startup scope list.
2363
- */
2364
- async deliveryStartupScopes(tenantIndex) {
2365
- if (tenantIndex.tenantMode === "single-tenant") {
2366
- return Object.freeze([Object.freeze({})]);
2367
- }
2368
- return Object.freeze((await tenantIndex.all()).map((tenantId) => Object.freeze({ tenantId })));
2369
- },
2370
- /**
2371
- * Creates tenant-validated storage context for a delivery scope.
2372
- *
2373
- * @param context Built context snapshot.
2374
- * @param scope Selected startup scope.
2375
- * @returns Storage context for the selected tenant.
2376
- */
2377
- deliveryStorageContext(context, scope) {
2378
- if (context.tenantMode === "single-tenant") {
2379
- if (scope.tenantId !== undefined) {
2380
- throw new Error(`Single-tenant context "${context.name.value}" does not accept tenantId.`);
2381
- }
2382
- return Object.freeze({ name: context.name.value, multitenant: false });
2383
- }
2384
- const tenantId = scope.tenantId;
2385
- if (tenantId === undefined) {
2386
- throw new Error(`Multitenant context "${context.name.value}" requires tenantId.`);
2387
- }
2388
- return Object.freeze({
2389
- name: context.name.value,
2390
- multitenant: true,
2391
- tenantId: TenantBoundary.from(tenantId).tenantId,
2392
2764
  });
2393
2765
  },
2394
2766
  /**
@@ -2488,7 +2860,7 @@ const ContextParts = Object.freeze({
2488
2860
  registeredRepositories.set(repository, { name: registration.name });
2489
2861
  },
2490
2862
  close: () => {
2491
- repositoryAccess.clearRuntime(repository);
2863
+ repositoryAccess.clearRuntimeIdle(repository);
2492
2864
  },
2493
2865
  };
2494
2866
  },
@@ -2502,6 +2874,7 @@ const ContextParts = Object.freeze({
2502
2874
  repositoryAccess.bindRuntime(repository, {
2503
2875
  context: registration.storageContext,
2504
2876
  storageFactory: registration.storageFactory,
2877
+ onAdmit: registration.onAdmit,
2505
2878
  ...(registration.ai === undefined ? {} : { ai: registration.ai }),
2506
2879
  stand: registration.stand,
2507
2880
  signalMetadata: new SignalMetadata(),
@@ -2511,11 +2884,11 @@ const ContextParts = Object.freeze({
2511
2884
  registerEventSchema: registration.registerEventSchema,
2512
2885
  registerSystemEventSchema: registration.registerSystemEventSchema,
2513
2886
  publishAgentEvent: registration.publishAgentEvent,
2514
- prepareSavedCommand: registration.prepareSavedCommand,
2515
- publishSavedCommand: registration.publishSavedCommand,
2516
2887
  publishAgentSystemEvent: registration.publishAgentSystemEvent,
2517
- recordAcceptedSaved: registration.recordAcceptedSaved,
2518
- wakeAcceptedAgent: registration.wakeAcceptedAgent,
2888
+ agentReceipts: registration.agentReceipts,
2889
+ agentSignal: registration.agentSignal,
2890
+ reserveAgent: registration.reserveAgent,
2891
+ deferAgent: registration.deferAgent,
2519
2892
  });
2520
2893
  },
2521
2894
  /**
@@ -2637,7 +3010,7 @@ const ContextParts = Object.freeze({
2637
3010
  });
2638
3011
  }
2639
3012
  finally {
2640
- eventStore.close();
3013
+ await eventStore.close();
2641
3014
  }
2642
3015
  },
2643
3016
  /**